|
|
1.1 ! root 1: #include "slirp.h" ! 2: ! 3: /* host address */ ! 4: struct in_addr our_addr; ! 5: /* host dns address */ ! 6: struct in_addr dns_addr; ! 7: /* host loopback address */ ! 8: struct in_addr loopback_addr; ! 9: ! 10: /* address for slirp virtual addresses */ ! 11: struct in_addr special_addr; ! 12: /* virtual address alias for host */ ! 13: struct in_addr alias_addr; ! 14: ! 15: const uint8_t special_ethaddr[6] = { ! 16: 0x52, 0x54, 0x00, 0x12, 0x35, 0x00 ! 17: }; ! 18: ! 19: uint8_t client_ethaddr[6]; ! 20: ! 21: int do_slowtimo; ! 22: int link_up; ! 23: struct timeval tt; ! 24: FILE *lfd; ! 25: struct ex_list *exec_list; ! 26: ! 27: /* XXX: suppress those select globals */ ! 28: fd_set *global_readfds, *global_writefds, *global_xfds; ! 29: ! 30: char slirp_hostname[33]; ! 31: ! 32: #ifdef _WIN32 ! 33: ! 34: static int get_dns_addr(struct in_addr *pdns_addr) ! 35: { ! 36: FIXED_INFO *FixedInfo=NULL; ! 37: ULONG BufLen; ! 38: DWORD ret; ! 39: IP_ADDR_STRING *pIPAddr; ! 40: struct in_addr tmp_addr; ! 41: ! 42: FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO)); ! 43: BufLen = sizeof(FIXED_INFO); ! 44: ! 45: if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) { ! 46: if (FixedInfo) { ! 47: GlobalFree(FixedInfo); ! 48: FixedInfo = NULL; ! 49: } ! 50: FixedInfo = GlobalAlloc(GPTR, BufLen); ! 51: } ! 52: ! 53: if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) { ! 54: printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret ); ! 55: if (FixedInfo) { ! 56: GlobalFree(FixedInfo); ! 57: FixedInfo = NULL; ! 58: } ! 59: return -1; ! 60: } ! 61: ! 62: pIPAddr = &(FixedInfo->DnsServerList); ! 63: inet_aton(pIPAddr->IpAddress.String, &tmp_addr); ! 64: *pdns_addr = tmp_addr; ! 65: #if 0 ! 66: printf( "DNS Servers:\n" ); ! 67: printf( "DNS Addr:%s\n", pIPAddr->IpAddress.String ); ! 68: ! 69: pIPAddr = FixedInfo -> DnsServerList.Next; ! 70: while ( pIPAddr ) { ! 71: printf( "DNS Addr:%s\n", pIPAddr ->IpAddress.String ); ! 72: pIPAddr = pIPAddr ->Next; ! 73: } ! 74: #endif ! 75: if (FixedInfo) { ! 76: GlobalFree(FixedInfo); ! 77: FixedInfo = NULL; ! 78: } ! 79: return 0; ! 80: } ! 81: ! 82: #else ! 83: ! 84: static int get_dns_addr(struct in_addr *pdns_addr) ! 85: { ! 86: char buff[512]; ! 87: char buff2[256]; ! 88: FILE *f; ! 89: int found = 0; ! 90: struct in_addr tmp_addr; ! 91: ! 92: f = fopen("/etc/resolv.conf", "r"); ! 93: if (!f) ! 94: return -1; ! 95: ! 96: lprint("IP address of your DNS(s): "); ! 97: while (fgets(buff, 512, f) != NULL) { ! 98: if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { ! 99: if (!inet_aton(buff2, &tmp_addr)) ! 100: continue; ! 101: if (tmp_addr.s_addr == loopback_addr.s_addr) ! 102: tmp_addr = our_addr; ! 103: /* If it's the first one, set it to dns_addr */ ! 104: if (!found) ! 105: *pdns_addr = tmp_addr; ! 106: else ! 107: lprint(", "); ! 108: if (++found > 3) { ! 109: lprint("(more)"); ! 110: break; ! 111: } else ! 112: lprint("%s", inet_ntoa(tmp_addr)); ! 113: } ! 114: } ! 115: fclose(f); ! 116: if (!found) ! 117: return -1; ! 118: return 0; ! 119: } ! 120: ! 121: #endif ! 122: ! 123: #ifdef _WIN32 ! 124: void slirp_cleanup(void) ! 125: { ! 126: WSACleanup(); ! 127: } ! 128: #endif ! 129: ! 130: int slirp_init(void) ! 131: { ! 132: // debug_init("/tmp/slirp.log", DEBUG_DEFAULT); ! 133: ! 134: #ifdef _WIN32 ! 135: { ! 136: WSADATA Data; ! 137: WSAStartup(MAKEWORD(2,0), &Data); ! 138: atexit(slirp_cleanup); ! 139: } ! 140: #endif ! 141: ! 142: link_up = 1; ! 143: ! 144: if_init(); ! 145: ip_init(); ! 146: ! 147: /* Initialise mbufs *after* setting the MTU */ ! 148: m_init(); ! 149: ! 150: /* set default addresses */ ! 151: inet_aton("127.0.0.1", &loopback_addr); ! 152: ! 153: if (get_dns_addr(&dns_addr) < 0) ! 154: return -1; ! 155: ! 156: inet_aton(CTL_SPECIAL, &special_addr); ! 157: alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS); ! 158: getouraddr(); ! 159: return 0; ! 160: } ! 161: ! 162: #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) ! 163: #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) ! 164: #define UPD_NFDS(x) if (nfds < (x)) nfds = (x) ! 165: ! 166: /* ! 167: * curtime kept to an accuracy of 1ms ! 168: */ ! 169: #ifdef _WIN32 ! 170: static void updtime(void) ! 171: { ! 172: struct _timeb tb; ! 173: ! 174: _ftime(&tb); ! 175: curtime = (u_int)tb.time * (u_int)1000; ! 176: curtime += (u_int)tb.millitm; ! 177: } ! 178: #else ! 179: static void updtime(void) ! 180: { ! 181: gettimeofday(&tt, 0); ! 182: ! 183: curtime = (u_int)tt.tv_sec * (u_int)1000; ! 184: curtime += (u_int)tt.tv_usec / (u_int)1000; ! 185: ! 186: if ((tt.tv_usec % 1000) >= 500) ! 187: curtime++; ! 188: } ! 189: #endif ! 190: ! 191: int slirp_select_fill(int *pnfds, ! 192: fd_set *readfds, fd_set *writefds, fd_set *xfds) ! 193: { ! 194: struct socket *so, *so_next; ! 195: int nfds; ! 196: int timeout, tmp_time; ! 197: ! 198: /* fail safe */ ! 199: global_readfds = NULL; ! 200: global_writefds = NULL; ! 201: global_xfds = NULL; ! 202: ! 203: nfds = *pnfds; ! 204: /* ! 205: * First, TCP sockets ! 206: */ ! 207: do_slowtimo = 0; ! 208: if (link_up) { ! 209: /* ! 210: * *_slowtimo needs calling if there are IP fragments ! 211: * in the fragment queue, or there are TCP connections active ! 212: */ ! 213: do_slowtimo = ((tcb.so_next != &tcb) || ! 214: (&ipq.ip_link != ipq.ip_link.next)); ! 215: ! 216: for (so = tcb.so_next; so != &tcb; so = so_next) { ! 217: so_next = so->so_next; ! 218: ! 219: /* ! 220: * See if we need a tcp_fasttimo ! 221: */ ! 222: if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) ! 223: time_fasttimo = curtime; /* Flag when we want a fasttimo */ ! 224: ! 225: /* ! 226: * NOFDREF can include still connecting to local-host, ! 227: * newly socreated() sockets etc. Don't want to select these. ! 228: */ ! 229: if (so->so_state & SS_NOFDREF || so->s == -1) ! 230: continue; ! 231: ! 232: /* ! 233: * Set for reading sockets which are accepting ! 234: */ ! 235: if (so->so_state & SS_FACCEPTCONN) { ! 236: FD_SET(so->s, readfds); ! 237: UPD_NFDS(so->s); ! 238: continue; ! 239: } ! 240: ! 241: /* ! 242: * Set for writing sockets which are connecting ! 243: */ ! 244: if (so->so_state & SS_ISFCONNECTING) { ! 245: FD_SET(so->s, writefds); ! 246: UPD_NFDS(so->s); ! 247: continue; ! 248: } ! 249: ! 250: /* ! 251: * Set for writing if we are connected, can send more, and ! 252: * we have something to send ! 253: */ ! 254: if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { ! 255: FD_SET(so->s, writefds); ! 256: UPD_NFDS(so->s); ! 257: } ! 258: ! 259: /* ! 260: * Set for reading (and urgent data) if we are connected, can ! 261: * receive more, and we have room for it XXX /2 ? ! 262: */ ! 263: if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { ! 264: FD_SET(so->s, readfds); ! 265: FD_SET(so->s, xfds); ! 266: UPD_NFDS(so->s); ! 267: } ! 268: } ! 269: ! 270: /* ! 271: * UDP sockets ! 272: */ ! 273: for (so = udb.so_next; so != &udb; so = so_next) { ! 274: so_next = so->so_next; ! 275: ! 276: /* ! 277: * See if it's timed out ! 278: */ ! 279: if (so->so_expire) { ! 280: if (so->so_expire <= curtime) { ! 281: udp_detach(so); ! 282: continue; ! 283: } else ! 284: do_slowtimo = 1; /* Let socket expire */ ! 285: } ! 286: ! 287: /* ! 288: * When UDP packets are received from over the ! 289: * link, they're sendto()'d straight away, so ! 290: * no need for setting for writing ! 291: * Limit the number of packets queued by this session ! 292: * to 4. Note that even though we try and limit this ! 293: * to 4 packets, the session could have more queued ! 294: * if the packets needed to be fragmented ! 295: * (XXX <= 4 ?) ! 296: */ ! 297: if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { ! 298: FD_SET(so->s, readfds); ! 299: UPD_NFDS(so->s); ! 300: } ! 301: } ! 302: } ! 303: ! 304: /* ! 305: * Setup timeout to use minimum CPU usage, especially when idle ! 306: */ ! 307: ! 308: timeout = -1; ! 309: ! 310: /* ! 311: * If a slowtimo is needed, set timeout to 5ms from the last ! 312: * slow timeout. If a fast timeout is needed, set timeout within ! 313: * 2ms of when it was requested. ! 314: */ ! 315: # define SLOW_TIMO 5 ! 316: # define FAST_TIMO 2 ! 317: if (do_slowtimo) { ! 318: timeout = (SLOW_TIMO - (curtime - last_slowtimo)) * 1000; ! 319: if (timeout < 0) ! 320: timeout = 0; ! 321: else if (timeout > (SLOW_TIMO * 1000)) ! 322: timeout = SLOW_TIMO * 1000; ! 323: ! 324: /* Can only fasttimo if we also slowtimo */ ! 325: if (time_fasttimo) { ! 326: tmp_time = (FAST_TIMO - (curtime - time_fasttimo)) * 1000; ! 327: if (tmp_time < 0) ! 328: tmp_time = 0; ! 329: ! 330: /* Choose the smallest of the 2 */ ! 331: if (tmp_time < timeout) ! 332: timeout = tmp_time; ! 333: } ! 334: } ! 335: *pnfds = nfds; ! 336: ! 337: /* ! 338: * Adjust the timeout to make the minimum timeout ! 339: * 2ms (XXX?) to lessen the CPU load ! 340: */ ! 341: if (timeout < (FAST_TIMO * 1000)) ! 342: timeout = FAST_TIMO * 1000; ! 343: ! 344: return timeout; ! 345: } ! 346: ! 347: void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds) ! 348: { ! 349: struct socket *so, *so_next; ! 350: int ret; ! 351: ! 352: global_readfds = readfds; ! 353: global_writefds = writefds; ! 354: global_xfds = xfds; ! 355: ! 356: /* Update time */ ! 357: updtime(); ! 358: ! 359: /* ! 360: * See if anything has timed out ! 361: */ ! 362: if (link_up) { ! 363: if (time_fasttimo && ((curtime - time_fasttimo) >= FAST_TIMO)) { ! 364: tcp_fasttimo(); ! 365: time_fasttimo = 0; ! 366: } ! 367: if (do_slowtimo && ((curtime - last_slowtimo) >= SLOW_TIMO)) { ! 368: ip_slowtimo(); ! 369: tcp_slowtimo(); ! 370: last_slowtimo = curtime; ! 371: } ! 372: } ! 373: ! 374: /* ! 375: * Check sockets ! 376: */ ! 377: if (link_up) { ! 378: /* ! 379: * Check TCP sockets ! 380: */ ! 381: for (so = tcb.so_next; so != &tcb; so = so_next) { ! 382: so_next = so->so_next; ! 383: ! 384: /* ! 385: * FD_ISSET is meaningless on these sockets ! 386: * (and they can crash the program) ! 387: */ ! 388: if (so->so_state & SS_NOFDREF || so->s == -1) ! 389: continue; ! 390: ! 391: /* ! 392: * Check for URG data ! 393: * This will soread as well, so no need to ! 394: * test for readfds below if this succeeds ! 395: */ ! 396: if (FD_ISSET(so->s, xfds)) ! 397: sorecvoob(so); ! 398: /* ! 399: * Check sockets for reading ! 400: */ ! 401: else if (FD_ISSET(so->s, readfds)) { ! 402: /* ! 403: * Check for incoming connections ! 404: */ ! 405: if (so->so_state & SS_FACCEPTCONN) { ! 406: tcp_connect(so); ! 407: continue; ! 408: } /* else */ ! 409: ret = soread(so); ! 410: ! 411: /* Output it if we read something */ ! 412: if (ret > 0) ! 413: tcp_output(sototcpcb(so)); ! 414: } ! 415: ! 416: /* ! 417: * Check sockets for writing ! 418: */ ! 419: if (FD_ISSET(so->s, writefds)) { ! 420: /* ! 421: * Check for non-blocking, still-connecting sockets ! 422: */ ! 423: if (so->so_state & SS_ISFCONNECTING) { ! 424: /* Connected */ ! 425: so->so_state &= ~SS_ISFCONNECTING; ! 426: ! 427: ret = send(so->s, &ret, 0, 0); ! 428: if (ret < 0) { ! 429: /* XXXXX Must fix, zero bytes is a NOP */ ! 430: if (errno == EAGAIN || errno == EWOULDBLOCK || ! 431: errno == EINPROGRESS || errno == ENOTCONN) ! 432: continue; ! 433: ! 434: /* else failed */ ! 435: so->so_state = SS_NOFDREF; ! 436: } ! 437: /* else so->so_state &= ~SS_ISFCONNECTING; */ ! 438: ! 439: /* ! 440: * Continue tcp_input ! 441: */ ! 442: tcp_input((struct mbuf *)NULL, sizeof(struct ip), so); ! 443: /* continue; */ ! 444: } else ! 445: ret = sowrite(so); ! 446: /* ! 447: * XXXXX If we wrote something (a lot), there ! 448: * could be a need for a window update. ! 449: * In the worst case, the remote will send ! 450: * a window probe to get things going again ! 451: */ ! 452: } ! 453: ! 454: /* ! 455: * Probe a still-connecting, non-blocking socket ! 456: * to check if it's still alive ! 457: */ ! 458: #ifdef PROBE_CONN ! 459: if (so->so_state & SS_ISFCONNECTING) { ! 460: ret = recv(so->s, (char *)&ret, 0,0); ! 461: ! 462: if (ret < 0) { ! 463: /* XXX */ ! 464: if (errno == EAGAIN || errno == EWOULDBLOCK || ! 465: errno == EINPROGRESS || errno == ENOTCONN) ! 466: continue; /* Still connecting, continue */ ! 467: ! 468: /* else failed */ ! 469: so->so_state = SS_NOFDREF; ! 470: ! 471: /* tcp_input will take care of it */ ! 472: } else { ! 473: ret = send(so->s, &ret, 0,0); ! 474: if (ret < 0) { ! 475: /* XXX */ ! 476: if (errno == EAGAIN || errno == EWOULDBLOCK || ! 477: errno == EINPROGRESS || errno == ENOTCONN) ! 478: continue; ! 479: /* else failed */ ! 480: so->so_state = SS_NOFDREF; ! 481: } else ! 482: so->so_state &= ~SS_ISFCONNECTING; ! 483: ! 484: } ! 485: tcp_input((struct mbuf *)NULL, sizeof(struct ip),so); ! 486: } /* SS_ISFCONNECTING */ ! 487: #endif ! 488: } ! 489: ! 490: /* ! 491: * Now UDP sockets. ! 492: * Incoming packets are sent straight away, they're not buffered. ! 493: * Incoming UDP data isn't buffered either. ! 494: */ ! 495: for (so = udb.so_next; so != &udb; so = so_next) { ! 496: so_next = so->so_next; ! 497: ! 498: if (so->s != -1 && FD_ISSET(so->s, readfds)) { ! 499: sorecvfrom(so); ! 500: } ! 501: } ! 502: } ! 503: ! 504: /* ! 505: * See if we can start outputting ! 506: */ ! 507: if (if_queued && link_up) ! 508: if_start(); ! 509: ! 510: /* clear global file descriptor sets. ! 511: * these reside on the stack in vl.c ! 512: * so they're unusable if we're not in ! 513: * slirp_select_fill or slirp_select_poll. ! 514: */ ! 515: global_readfds = NULL; ! 516: global_writefds = NULL; ! 517: global_xfds = NULL; ! 518: } ! 519: ! 520: #define ETH_ALEN 6 ! 521: #define ETH_HLEN 14 ! 522: ! 523: #define ETH_P_IP 0x0800 /* Internet Protocol packet */ ! 524: #define ETH_P_ARP 0x0806 /* Address Resolution packet */ ! 525: ! 526: #define ARPOP_REQUEST 1 /* ARP request */ ! 527: #define ARPOP_REPLY 2 /* ARP reply */ ! 528: ! 529: struct ethhdr ! 530: { ! 531: unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ ! 532: unsigned char h_source[ETH_ALEN]; /* source ether addr */ ! 533: unsigned short h_proto; /* packet type ID field */ ! 534: }; ! 535: ! 536: struct arphdr ! 537: { ! 538: unsigned short ar_hrd; /* format of hardware address */ ! 539: unsigned short ar_pro; /* format of protocol address */ ! 540: unsigned char ar_hln; /* length of hardware address */ ! 541: unsigned char ar_pln; /* length of protocol address */ ! 542: unsigned short ar_op; /* ARP opcode (command) */ ! 543: ! 544: /* ! 545: * Ethernet looks like this : This bit is variable sized however... ! 546: */ ! 547: unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ ! 548: unsigned char ar_sip[4]; /* sender IP address */ ! 549: unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ ! 550: unsigned char ar_tip[4]; /* target IP address */ ! 551: }; ! 552: ! 553: void arp_input(const uint8_t *pkt, int pkt_len) ! 554: { ! 555: struct ethhdr *eh = (struct ethhdr *)pkt; ! 556: struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN); ! 557: uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)]; ! 558: struct ethhdr *reh = (struct ethhdr *)arp_reply; ! 559: struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN); ! 560: int ar_op; ! 561: struct ex_list *ex_ptr; ! 562: ! 563: ar_op = ntohs(ah->ar_op); ! 564: switch(ar_op) { ! 565: case ARPOP_REQUEST: ! 566: if (!memcmp(ah->ar_tip, &special_addr, 3)) { ! 567: if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS) ! 568: goto arp_ok; ! 569: for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { ! 570: if (ex_ptr->ex_addr == ah->ar_tip[3]) ! 571: goto arp_ok; ! 572: } ! 573: return; ! 574: arp_ok: ! 575: /* XXX: make an ARP request to have the client address */ ! 576: memcpy(client_ethaddr, eh->h_source, ETH_ALEN); ! 577: ! 578: /* ARP request for alias/dns mac address */ ! 579: memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); ! 580: memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1); ! 581: reh->h_source[5] = ah->ar_tip[3]; ! 582: reh->h_proto = htons(ETH_P_ARP); ! 583: ! 584: rah->ar_hrd = htons(1); ! 585: rah->ar_pro = htons(ETH_P_IP); ! 586: rah->ar_hln = ETH_ALEN; ! 587: rah->ar_pln = 4; ! 588: rah->ar_op = htons(ARPOP_REPLY); ! 589: memcpy(rah->ar_sha, reh->h_source, ETH_ALEN); ! 590: memcpy(rah->ar_sip, ah->ar_tip, 4); ! 591: memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); ! 592: memcpy(rah->ar_tip, ah->ar_sip, 4); ! 593: slirp_output(arp_reply, sizeof(arp_reply)); ! 594: } ! 595: break; ! 596: default: ! 597: break; ! 598: } ! 599: } ! 600: ! 601: void slirp_input(const uint8_t *pkt, int pkt_len) ! 602: { ! 603: struct mbuf *m; ! 604: int proto; ! 605: ! 606: if (pkt_len < ETH_HLEN) ! 607: return; ! 608: ! 609: proto = (pkt[12] << 8) | pkt[13]; ! 610: switch(proto) { ! 611: case ETH_P_ARP: ! 612: arp_input(pkt, pkt_len); ! 613: break; ! 614: case ETH_P_IP: ! 615: m = m_get(); ! 616: if (!m) ! 617: return; ! 618: /* Note: we add to align the IP header */ ! 619: m->m_len = pkt_len + 2; ! 620: memcpy(m->m_data + 2, pkt, pkt_len); ! 621: ! 622: m->m_data += 2 + ETH_HLEN; ! 623: m->m_len -= 2 + ETH_HLEN; ! 624: ! 625: ip_input(m); ! 626: break; ! 627: default: ! 628: break; ! 629: } ! 630: } ! 631: ! 632: /* output the IP packet to the ethernet device */ ! 633: void if_encap(const uint8_t *ip_data, int ip_data_len) ! 634: { ! 635: uint8_t buf[1600]; ! 636: struct ethhdr *eh = (struct ethhdr *)buf; ! 637: ! 638: if (ip_data_len + ETH_HLEN > sizeof(buf)) ! 639: return; ! 640: ! 641: memcpy(eh->h_dest, client_ethaddr, ETH_ALEN); ! 642: memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1); ! 643: /* XXX: not correct */ ! 644: eh->h_source[5] = CTL_ALIAS; ! 645: eh->h_proto = htons(ETH_P_IP); ! 646: memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len); ! 647: slirp_output(buf, ip_data_len + ETH_HLEN); ! 648: } ! 649: ! 650: int slirp_redir(int is_udp, int host_port, ! 651: struct in_addr guest_addr, int guest_port) ! 652: { ! 653: if (is_udp) { ! 654: if (!udp_listen(htons(host_port), guest_addr.s_addr, ! 655: htons(guest_port), 0)) ! 656: return -1; ! 657: } else { ! 658: if (!solisten(htons(host_port), guest_addr.s_addr, ! 659: htons(guest_port), 0)) ! 660: return -1; ! 661: } ! 662: return 0; ! 663: } ! 664: ! 665: int slirp_add_exec(int do_pty, const char *args, int addr_low_byte, ! 666: int guest_port) ! 667: { ! 668: return add_exec(&exec_list, do_pty, (char *)args, ! 669: addr_low_byte, htons(guest_port)); ! 670: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.