Path: blob/a-new-beginning/SharedDependencies/Sources/libslirp/tcp_subr.c
2 views
/* SPDX-License-Identifier: BSD-3-Clause */1/*2* Copyright (c) 1982, 1986, 1988, 1990, 19933* The Regents of the University of California. All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13* 3. Neither the name of the University nor the names of its contributors14* may be used to endorse or promote products derived from this software15* without specific prior written permission.16*17* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*29* @(#)tcp_subr.c 8.1 (Berkeley) 6/10/9330* tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp31*/3233/*34* Changes and additions relating to SLiRP35* Copyright (c) 1995 Danny Gasparovski.36*/3738#include "slirp.h"3940/* patchable/settable parameters for tcp */41/* Don't do rfc1323 performance enhancements */42#define TCP_DO_RFC1323 04344/*45* Tcp initialization46*/47void tcp_init(Slirp *slirp)48{49slirp->tcp_iss = 1; /* wrong */50slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb;51slirp->tcp_last_so = &slirp->tcb;52}5354void tcp_cleanup(Slirp *slirp)55{56while (slirp->tcb.so_next != &slirp->tcb) {57tcp_close(sototcpcb(slirp->tcb.so_next));58}59}6061void tcp_template(struct tcpcb *tp)62{63struct socket *so = tp->t_socket;64register struct tcpiphdr *n = &tp->t_template;6566n->ti_mbuf = NULL;67memset(&n->ti, 0, sizeof(n->ti));68n->ti_x0 = 0;69switch (so->so_ffamily) {70case AF_INET:71n->ti_pr = IPPROTO_TCP;72n->ti_len = htons(sizeof(struct tcphdr));73n->ti_src = so->so_faddr;74n->ti_dst = so->so_laddr;75n->ti_sport = so->so_fport;76n->ti_dport = so->so_lport;77break;7879case AF_INET6:80n->ti_nh6 = IPPROTO_TCP;81n->ti_len = htons(sizeof(struct tcphdr));82n->ti_src6 = so->so_faddr6;83n->ti_dst6 = so->so_laddr6;84n->ti_sport = so->so_fport6;85n->ti_dport = so->so_lport6;86break;8788default:89g_assert_not_reached();90}9192n->ti_seq = 0;93n->ti_ack = 0;94n->ti_x2 = 0;95n->ti_off = 5;96n->ti_flags = 0;97n->ti_win = 0;98n->ti_sum = 0;99n->ti_urp = 0;100}101102/*103* Send a single message to the TCP at address specified by104* the given TCP/IP header. If m == 0, then we make a copy105* of the tcpiphdr at ti and send directly to the addressed host.106* This is used to force keep alive messages out using the TCP107* template for a connection tp->t_template. If flags are given108* then we send a message back to the TCP which originated the109* segment ti, and discard the mbuf containing it and any other110* attached mbufs.111*112* In any case the ack and sequence number of the transmitted113* segment are as specified by the parameters.114*/115void tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,116tcp_seq ack, tcp_seq seq, int flags, unsigned short af)117{118register int tlen;119int win = 0;120121DEBUG_CALL("tcp_respond");122DEBUG_ARG("tp = %p", tp);123DEBUG_ARG("ti = %p", ti);124DEBUG_ARG("m = %p", m);125DEBUG_ARG("ack = %u", ack);126DEBUG_ARG("seq = %u", seq);127DEBUG_ARG("flags = %x", flags);128129if (tp)130win = sbspace(&tp->t_socket->so_rcv);131if (m == NULL) {132if (!tp || (m = m_get(tp->t_socket->slirp)) == NULL)133return;134tlen = 0;135m->m_data += IF_MAXLINKHDR;136*mtod(m, struct tcpiphdr *) = *ti;137ti = mtod(m, struct tcpiphdr *);138switch (af) {139case AF_INET:140ti->ti.ti_i4.ih_x1 = 0;141break;142case AF_INET6:143ti->ti.ti_i6.ih_x1 = 0;144break;145default:146g_assert_not_reached();147}148flags = TH_ACK;149} else {150/*151* ti points into m so the next line is just making152* the mbuf point to ti153*/154m->m_data = (char *)ti;155156m->m_len = sizeof(struct tcpiphdr);157tlen = 0;158#define xchg(a, b, type) \159{ \160type t; \161t = a; \162a = b; \163b = t; \164}165switch (af) {166case AF_INET:167xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);168xchg(ti->ti_dport, ti->ti_sport, uint16_t);169break;170case AF_INET6:171xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr);172xchg(ti->ti_dport, ti->ti_sport, uint16_t);173break;174default:175g_assert_not_reached();176}177#undef xchg178}179ti->ti_len = htons((uint16_t)(sizeof(struct tcphdr) + tlen));180tlen += sizeof(struct tcpiphdr);181m->m_len = tlen;182183ti->ti_mbuf = NULL;184ti->ti_x0 = 0;185ti->ti_seq = htonl(seq);186ti->ti_ack = htonl(ack);187ti->ti_x2 = 0;188ti->ti_off = sizeof(struct tcphdr) >> 2;189ti->ti_flags = flags;190if (tp)191ti->ti_win = htons((uint16_t)(win >> tp->rcv_scale));192else193ti->ti_win = htons((uint16_t)win);194ti->ti_urp = 0;195ti->ti_sum = 0;196ti->ti_sum = cksum(m, tlen);197198struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *));199struct ip *ip;200struct ip6 *ip6;201202switch (af) {203case AF_INET:204m->m_data +=205sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip);206m->m_len -=207sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip);208ip = mtod(m, struct ip *);209ip->ip_len = m->m_len;210ip->ip_dst = tcpiph_save.ti_dst;211ip->ip_src = tcpiph_save.ti_src;212ip->ip_p = tcpiph_save.ti_pr;213214if (flags & TH_RST) {215ip->ip_ttl = MAXTTL;216} else {217ip->ip_ttl = IPDEFTTL;218}219220ip_output(NULL, m);221break;222223case AF_INET6:224m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) -225sizeof(struct ip6);226m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) -227sizeof(struct ip6);228ip6 = mtod(m, struct ip6 *);229ip6->ip_pl = tcpiph_save.ti_len;230ip6->ip_dst = tcpiph_save.ti_dst6;231ip6->ip_src = tcpiph_save.ti_src6;232ip6->ip_nh = tcpiph_save.ti_nh6;233234ip6_output(NULL, m, 0);235break;236237default:238g_assert_not_reached();239}240}241242struct tcpcb *tcp_newtcpcb(struct socket *so)243{244register struct tcpcb *tp;245246tp = g_new0(struct tcpcb, 1);247tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp;248/*249* 40: length of IPv4 header (20) + TCP header (20)250* 60: length of IPv6 header (40) + TCP header (20)251*/252tp->t_maxseg =253MIN(so->slirp->if_mtu - ((so->so_ffamily == AF_INET) ? 40 : 60),254TCP_MAXSEG_MAX);255256tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE | TF_REQ_TSTMP) : 0;257tp->t_socket = so;258259/*260* Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no261* rtt estimate. Set rttvar so that srtt + 2 * rttvar gives262* reasonable initial retransmit time.263*/264tp->t_srtt = TCPTV_SRTTBASE;265tp->t_rttvar = TCPTV_SRTTDFLT << 2;266tp->t_rttmin = TCPTV_MIN;267268TCPT_RANGESET(tp->t_rxtcur,269((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,270TCPTV_MIN, TCPTV_REXMTMAX);271272tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;273tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;274tp->t_state = TCPS_CLOSED;275276so->so_tcpcb = tp;277278return (tp);279}280281struct tcpcb *tcp_drop(struct tcpcb *tp, int err)282{283DEBUG_CALL("tcp_drop");284DEBUG_ARG("tp = %p", tp);285DEBUG_ARG("errno = %d", errno);286287if (TCPS_HAVERCVDSYN(tp->t_state)) {288tp->t_state = TCPS_CLOSED;289tcp_output(tp);290}291return (tcp_close(tp));292}293294struct tcpcb *tcp_close(struct tcpcb *tp)295{296register struct tcpiphdr *t;297struct socket *so = tp->t_socket;298Slirp *slirp = so->slirp;299register struct mbuf *m;300301DEBUG_CALL("tcp_close");302DEBUG_ARG("tp = %p", tp);303304/* free the reassembly queue, if any */305t = tcpfrag_list_first(tp);306while (!tcpfrag_list_end(t, tp)) {307t = tcpiphdr_next(t);308m = tcpiphdr_prev(t)->ti_mbuf;309slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(t)));310m_free(m);311}312g_free(tp);313so->so_tcpcb = NULL;314/* clobber input socket cache if we're closing the cached connection */315if (so == slirp->tcp_last_so)316slirp->tcp_last_so = &slirp->tcb;317so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);318closesocket(so->s);319sbfree(&so->so_rcv);320sbfree(&so->so_snd);321sofree(so);322return ((struct tcpcb *)0);323}324325/*326* TCP protocol interface to socket abstraction.327*/328329/*330* User issued close, and wish to trail through shutdown states:331* if never received SYN, just forget it. If got a SYN from peer,332* but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.333* If already got a FIN from peer, then almost done; go to LAST_ACK334* state. In all other cases, have already sent FIN to peer (e.g.335* after PRU_SHUTDOWN), and just have to play tedious game waiting336* for peer to send FIN or not respond to keep-alives, etc.337* We can let the user exit from the close as soon as the FIN is acked.338*/339void tcp_sockclosed(struct tcpcb *tp)340{341DEBUG_CALL("tcp_sockclosed");342DEBUG_ARG("tp = %p", tp);343344if (!tp) {345return;346}347348switch (tp->t_state) {349case TCPS_CLOSED:350case TCPS_LISTEN:351case TCPS_SYN_SENT:352tp->t_state = TCPS_CLOSED;353tcp_close(tp);354return;355356case TCPS_SYN_RECEIVED:357case TCPS_ESTABLISHED:358tp->t_state = TCPS_FIN_WAIT_1;359break;360361case TCPS_CLOSE_WAIT:362tp->t_state = TCPS_LAST_ACK;363break;364}365tcp_output(tp);366}367368/*369* Only do a connect, the tcp fields will be set in tcp_input370* return 0 if there's a result of the connect,371* else return -1 means we're still connecting372* The return value is almost always -1 since the socket is373* nonblocking. Connect returns after the SYN is sent, and does374* not wait for ACK+SYN.375*/376int tcp_fconnect(struct socket *so, unsigned short af)377{378int ret = 0;379380DEBUG_CALL("tcp_fconnect");381DEBUG_ARG("so = %p", so);382383ret = so->s = slirp_socket(af, SOCK_STREAM, 0);384if (ret >= 0) {385ret = slirp_bind_outbound(so, af);386if (ret < 0) {387// bind failed - close socket388closesocket(so->s);389so->s = -1;390return (ret);391}392}393394if (ret >= 0) {395int opt, s = so->s;396struct sockaddr_storage addr;397398slirp_set_nonblock(s);399so->slirp->cb->register_poll_fd(s, so->slirp->opaque);400slirp_socket_set_fast_reuse(s);401opt = 1;402setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));403opt = 1;404setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));405406addr = so->fhost.ss;407DEBUG_CALL(" connect()ing");408if (sotranslate_out(so, &addr) < 0) {409return -1;410}411412/* We don't care what port we get */413ret = connect(s, (struct sockaddr *)&addr, sockaddr_size(&addr));414415/*416* If it's not in progress, it failed, so we just return 0,417* without clearing SS_NOFDREF418*/419soisfconnecting(so);420}421422return (ret);423}424425/*426* We have a problem. The correct thing to do would be427* to first connect to the local-host, and only if the428* connection is accepted, then do an accept() here.429* But, a) we need to know who's trying to connect430* to the socket to be able to SYN the local-host, and431* b) we are already connected to the foreign host by432* the time it gets to accept(), so... We simply accept433* here and SYN the local-host.434*/435void tcp_connect(struct socket *inso)436{437Slirp *slirp = inso->slirp;438struct socket *so;439struct sockaddr_storage addr;440socklen_t addrlen;441struct tcpcb *tp;442int s, opt, ret;443/* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */444char addrstr[INET6_ADDRSTRLEN];445char portstr[6];446447DEBUG_CALL("tcp_connect");448DEBUG_ARG("inso = %p", inso);449switch (inso->lhost.ss.ss_family) {450case AF_INET:451addrlen = sizeof(struct sockaddr_in);452break;453case AF_INET6:454addrlen = sizeof(struct sockaddr_in6);455break;456default:457g_assert_not_reached();458}459ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, addrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);460g_assert(ret == 0);461DEBUG_ARG("ip = [%s]:%s", addrstr, portstr);462DEBUG_ARG("so_state = 0x%x", inso->so_state);463464/* Perform lazy guest IP address resolution if needed. */465if (inso->so_state & SS_HOSTFWD) {466/*467* We can only reject the connection request by accepting it and468* then immediately closing it. Note that SS_FACCEPTONCE sockets can't469* get here.470*/471if (soassign_guest_addr_if_needed(inso) < 0) {472/*473* Guest address isn't available yet. We could either try to defer474* completing this connection request until the guest address is475* available, or punt. It's easier to punt. Otherwise we need to476* complicate the mechanism by which we're called to defer calling477* us again until the guest address is available.478*/479DEBUG_MISC(" guest address not available yet");480addrlen = sizeof(addr);481s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);482if (s >= 0) {483close(s);484}485return;486}487}488489/*490* If it's an SS_ACCEPTONCE socket, no need to socreate()491* another socket, just use the accept() socket.492*/493if (inso->so_state & SS_FACCEPTONCE) {494/* FACCEPTONCE already have a tcpcb */495so = inso;496} else {497so = socreate(slirp, IPPROTO_TCP);498tcp_attach(so);499so->lhost = inso->lhost;500so->so_ffamily = inso->so_ffamily;501}502503tcp_mss(sototcpcb(so), 0);504505addrlen = sizeof(addr);506s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);507if (s < 0) {508tcp_close(sototcpcb(so)); /* This will sofree() as well */509return;510}511slirp_set_nonblock(s);512so->slirp->cb->register_poll_fd(s, so->slirp->opaque);513slirp_socket_set_fast_reuse(s);514opt = 1;515setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));516slirp_socket_set_nodelay(s);517518so->fhost.ss = addr;519sotranslate_accept(so);520521/* Close the accept() socket, set right state */522if (inso->so_state & SS_FACCEPTONCE) {523/* If we only accept once, close the accept() socket */524so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);525closesocket(so->s);526527/* Don't select it yet, even though we have an FD */528/* if it's not FACCEPTONCE, it's already NOFDREF */529so->so_state = SS_NOFDREF;530}531so->s = s;532so->so_state |= SS_INCOMING;533534so->so_iptos = tcp_tos(so);535tp = sototcpcb(so);536537tcp_template(tp);538539tp->t_state = TCPS_SYN_SENT;540tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;541tp->iss = slirp->tcp_iss;542slirp->tcp_iss += TCP_ISSINCR / 2;543tcp_sendseqinit(tp);544tcp_output(tp);545}546547void tcp_attach(struct socket *so)548{549so->so_tcpcb = tcp_newtcpcb(so);550slirp_insque(so, &so->slirp->tcb);551}552553/*554* Set the socket's type of service field555*/556static const struct tos_t tcptos[] = {557{ 0, 20, IPTOS_THROUGHPUT, 0 }, /* ftp data */558{ 21, 21, IPTOS_LOWDELAY, EMU_FTP }, /* ftp control */559{ 0, 23, IPTOS_LOWDELAY, 0 }, /* telnet */560{ 0, 80, IPTOS_THROUGHPUT, 0 }, /* WWW */561{ 0, 513, IPTOS_LOWDELAY, EMU_RLOGIN | EMU_NOCONNECT }, /* rlogin */562{ 0, 544, IPTOS_LOWDELAY, EMU_KSH }, /* kshell */563{ 0, 543, IPTOS_LOWDELAY, 0 }, /* klogin */564{ 0, 6667, IPTOS_THROUGHPUT, EMU_IRC }, /* IRC */565{ 0, 6668, IPTOS_THROUGHPUT, EMU_IRC }, /* IRC undernet */566{ 0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */567{ 0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */568{ 0, 0, 0, 0 }569};570571uint8_t tcp_tos(struct socket *so)572{573int i = 0;574575while (tcptos[i].tos) {576if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) ||577(tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) {578if (so->slirp->enable_emu)579so->so_emu = tcptos[i].emu;580return tcptos[i].tos;581}582i++;583}584return 0;585}586587/*588* NOTE: It's possible to crash SLiRP by sending it589* unstandard strings to emulate... if this is a problem,590* more checks are needed here591*592* XXX Assumes the whole command came in one packet593* XXX If there is more than one command in the packet, the others may594* be truncated.595* XXX If the command is too long, it may be truncated.596*597* XXX Some ftp clients will have their TOS set to598* LOWDELAY and so Nagel will kick in. Because of this,599* we'll get the first letter, followed by the rest, so600* we simply scan for ORT instead of PORT...601* DCC doesn't have this problem because there's other stuff602* in the packet before the DCC command.603*604* Return 1 if the mbuf m is still valid and should be605* sbappend()ed606*607* NOTE: if you return 0 you MUST m_free() the mbuf!608*/609int tcp_emu(struct socket *so, struct mbuf *m)610{611Slirp *slirp = so->slirp;612unsigned n1, n2, n3, n4, n5, n6;613char buff[257];614uint32_t laddr;615unsigned lport;616char *bptr;617618DEBUG_CALL("tcp_emu");619DEBUG_ARG("so = %p", so);620DEBUG_ARG("m = %p", m);621622switch (so->so_emu) {623int x, i;624625/* TODO: IPv6 */626case EMU_IDENT:627/*628* Identification protocol as per rfc-1413629*/630631{632struct socket *tmpso;633struct sockaddr_in addr;634socklen_t addrlen = sizeof(struct sockaddr_in);635char *eol = g_strstr_len(m->m_data, m->m_len, "\r\n");636637if (!eol) {638return 1;639}640641*eol = '\0';642if (sscanf(m->m_data, "%u%*[ ,]%u", &n1, &n2) == 2) {643HTONS(n1);644HTONS(n2);645/* n2 is the one on our host */646for (tmpso = slirp->tcb.so_next; tmpso != &slirp->tcb;647tmpso = tmpso->so_next) {648if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr &&649tmpso->so_lport == n2 &&650tmpso->so_faddr.s_addr == so->so_faddr.s_addr &&651tmpso->so_fport == n1) {652if (getsockname(tmpso->s, (struct sockaddr *)&addr,653&addrlen) == 0)654n2 = addr.sin_port;655break;656}657}658NTOHS(n1);659NTOHS(n2);660m_inc(m, g_snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1);661m->m_len = slirp_fmt(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2);662} else {663*eol = '\r';664}665666return 1;667}668669case EMU_FTP: /* ftp */670m_inc(m, m->m_len + 1);671*(m->m_data + m->m_len) = 0; /* NUL terminate for strstr */672if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {673/*674* Need to emulate the PORT command675*/676x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256[^\177]", &n1, &n2,677&n3, &n4, &n5, &n6, buff);678if (x < 6)679return 1;680681laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));682lport = htons((n5 << 8) | (n6));683684if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr, lport,685SS_FACCEPTONCE)) == NULL) {686return 1;687}688n6 = ntohs(so->so_fport);689690n5 = (n6 >> 8) & 0xff;691n6 &= 0xff;692693laddr = ntohl(so->so_faddr.s_addr);694695n1 = ((laddr >> 24) & 0xff);696n2 = ((laddr >> 16) & 0xff);697n3 = ((laddr >> 8) & 0xff);698n4 = (laddr & 0xff);699700m->m_len = bptr - m->m_data; /* Adjust length */701m->m_len += slirp_fmt(bptr, M_FREEROOM(m),702"ORT %d,%d,%d,%d,%d,%d\r\n%s",703n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");704return 1;705} else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {706/*707* Need to emulate the PASV response708*/709x = sscanf(710bptr,711"27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256[^\177]",712&n1, &n2, &n3, &n4, &n5, &n6, buff);713if (x < 6)714return 1;715716laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));717lport = htons((n5 << 8) | (n6));718719if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr, lport,720SS_FACCEPTONCE)) == NULL) {721return 1;722}723n6 = ntohs(so->so_fport);724725n5 = (n6 >> 8) & 0xff;726n6 &= 0xff;727728laddr = ntohl(so->so_faddr.s_addr);729730n1 = ((laddr >> 24) & 0xff);731n2 = ((laddr >> 16) & 0xff);732n3 = ((laddr >> 8) & 0xff);733n4 = (laddr & 0xff);734735m->m_len = bptr - m->m_data; /* Adjust length */736m->m_len += slirp_fmt(bptr, M_FREEROOM(m),737"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",738n1, n2, n3, n4, n5, n6, x == 7 ? buff : "");739return 1;740}741742return 1;743744case EMU_KSH:745/*746* The kshell (Kerberos rsh) and shell services both pass747* a local port port number to carry signals to the server748* and stderr to the client. It is passed at the beginning749* of the connection as a NUL-terminated decimal ASCII string.750*/751so->so_emu = 0;752for (lport = 0, i = 0; i < m->m_len - 1; ++i) {753if (m->m_data[i] < '0' || m->m_data[i] > '9')754return 1; /* invalid number */755lport *= 10;756lport += m->m_data[i] - '0';757}758if (m->m_data[m->m_len - 1] == '\0' && lport != 0 &&759(so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,760htons(lport), SS_FACCEPTONCE)) != NULL)761m->m_len = slirp_fmt0(m->m_data, M_ROOM(m),762"%d", ntohs(so->so_fport));763return 1;764765case EMU_IRC:766/*767* Need to emulate DCC CHAT, DCC SEND and DCC MOVE768*/769m_inc(m, m->m_len + 1);770*(m->m_data + m->m_len) = 0; /* NULL terminate the string for strstr */771if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)772return 1;773774/* The %256s is for the broken mIRC */775if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {776if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr),777htons(lport), SS_FACCEPTONCE)) == NULL) {778return 1;779}780m->m_len = bptr - m->m_data; /* Adjust length */781m->m_len += slirp_fmt(bptr, M_FREEROOM(m),782"DCC CHAT chat %lu %u%c\n",783(unsigned long)ntohl(so->so_faddr.s_addr),784ntohs(so->so_fport), 1);785} else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport,786&n1) == 4) {787if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr),788htons(lport), SS_FACCEPTONCE)) == NULL) {789return 1;790}791m->m_len = bptr - m->m_data; /* Adjust length */792m->m_len += slirp_fmt(bptr, M_FREEROOM(m),793"DCC SEND %s %lu %u %u%c\n", buff,794(unsigned long)ntohl(so->so_faddr.s_addr),795ntohs(so->so_fport), n1, 1);796} else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport,797&n1) == 4) {798if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr),799htons(lport), SS_FACCEPTONCE)) == NULL) {800return 1;801}802m->m_len = bptr - m->m_data; /* Adjust length */803m->m_len += slirp_fmt(bptr, M_FREEROOM(m),804"DCC MOVE %s %lu %u %u%c\n", buff,805(unsigned long)ntohl(so->so_faddr.s_addr),806ntohs(so->so_fport), n1, 1);807}808return 1;809810case EMU_REALAUDIO:811/*812* RealAudio emulation - JP. We must try to parse the incoming813* data and try to find the two characters that contain the814* port number. Then we redirect an udp port and replace the815* number with the real port we got.816*817* The 1.0 beta versions of the player are not supported818* any more.819*820* A typical packet for player version 1.0 (release version):821*822* 0000:50 4E 41 00 05823* 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 ........g.l.c..P824* 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH825* 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v826* 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB827*828* Now the port number 0x1BD7 is found at offset 0x04 of the829* Now the port number 0x1BD7 is found at offset 0x04 of the830* second packet. This time we received five bytes first and831* then the rest. You never know how many bytes you get.832*833* A typical packet for player version 2.0 (beta):834*835* 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA.............836* 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .gux.c..Win2.0.0837* 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/838* 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas839* 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B840*841* Port number 0x1BC1 is found at offset 0x0d.842*843* This is just a horrible switch statement. Variable ra tells844* us where we're going.845*/846847bptr = m->m_data;848while (bptr < m->m_data + m->m_len) {849uint16_t p;850static int ra = 0;851char ra_tbl[4];852853ra_tbl[0] = 0x50;854ra_tbl[1] = 0x4e;855ra_tbl[2] = 0x41;856ra_tbl[3] = 0;857858switch (ra) {859case 0:860case 2:861case 3:862if (*bptr++ != ra_tbl[ra]) {863ra = 0;864continue;865}866break;867868case 1:869/*870* We may get 0x50 several times, ignore them871*/872if (*bptr == 0x50) {873ra = 1;874bptr++;875continue;876} else if (*bptr++ != ra_tbl[ra]) {877ra = 0;878continue;879}880break;881882case 4:883/*884* skip version number885*/886bptr++;887break;888889case 5:890if (bptr == m->m_data + m->m_len - 1)891return 1; /* We need two bytes */892893/*894* The difference between versions 1.0 and895* 2.0 is here. For future versions of896* the player this may need to be modified.897*/898if (*(bptr + 1) == 0x02)899bptr += 8;900else901bptr += 4;902break;903904case 6:905/* This is the field containing the port906* number that RA-player is listening to.907*/908909if (bptr == m->m_data + m->m_len - 1)910return 1; /* We need two bytes */911912lport = (((uint8_t *)bptr)[0] << 8) + ((uint8_t *)bptr)[1];913if (lport < 6970)914lport += 256; /* don't know why */915if (lport < 6970 || lport > 7170)916return 1; /* failed */917918/* try to get udp port between 6970 - 7170 */919for (p = 6970; p < 7071; p++) {920if (udp_listen(slirp, INADDR_ANY, htons(p),921so->so_laddr.s_addr, htons(lport),922SS_FACCEPTONCE)) {923break;924}925}926if (p == 7071)927p = 0;928*(uint8_t *)bptr++ = (p >> 8) & 0xff;929*(uint8_t *)bptr = p & 0xff;930ra = 0;931return 1; /* port redirected, we're done */932break;933934default:935ra = 0;936}937ra++;938}939return 1;940941default:942/* Ooops, not emulated, won't call tcp_emu again */943so->so_emu = 0;944return 1;945}946}947948/*949* Do misc. config of SLiRP while its running.950* Return 0 if this connections is to be closed, 1 otherwise,951* return 2 if this is a command-line connection952*/953int tcp_ctl(struct socket *so)954{955Slirp *slirp = so->slirp;956struct sbuf *sb = &so->so_snd;957struct gfwd_list *ex_ptr;958959DEBUG_CALL("tcp_ctl");960DEBUG_ARG("so = %p", so);961962/* TODO: IPv6 */963if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {964/* Check if it's pty_exec */965for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {966if (ex_ptr->ex_fport == so->so_fport &&967so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {968if (ex_ptr->write_cb) {969so->s = -1;970so->guestfwd = ex_ptr;971return 1;972}973DEBUG_MISC(" executing %s", ex_ptr->ex_exec);974if (ex_ptr->ex_unix)975return open_unix(so, ex_ptr->ex_unix);976else977return fork_exec(so, ex_ptr->ex_exec);978}979}980}981sb->sb_cc = slirp_fmt(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data),982"Error: No application configured.\r\n");983sb->sb_wptr += sb->sb_cc;984return 0;985}986987988