commit b3dee48c9ccf35f3231d41057a8d3faceca784f3 Author: waltje Date: Sun Nov 4 17:37:57 2018 -0500 Initial upload of modified SLiRP diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2b7a7a1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,37 @@ +# +# Set up for native EOL's with automatic conversion for text files. +# + +# Autodetect text files +* text=auto + +# Definitively text files. +*.txt text +*.md text + +# Standard C source files. +*.c text +*.cc text +*.cpp text +*.h text + +# UNIX makefiles. +makefile* text +Makefile* text + +# MS Visual Studio diles. +*.mpp text +*.rc text +*.props text +*.vcxproj text +*.vcxproj.user text +*.vcxproj.filters text +*.sln text + +# Binary files; ensure those won't be messed with. +*.ico binary +*.jpg binary +*.png binary +*.xpm binary + +# End of file. diff --git a/.travis-build.sh b/.travis-build.sh new file mode 100644 index 0000000..b4acac1 --- /dev/null +++ b/.travis-build.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# +# VARCem Virtual ARchaeological Computer EMulator. +# An emulator of (mostly) x86-based PC systems and devices, +# using the ISA,EISA,VLB,MCA and PCI system buses, roughly +# spanning the era between 1981 and 1995. +# +# This file is part of the VARCem Project. +# +# Build script for the Travis CI remote builder service. +# +# Version: @(#).travis-build.sh 1.0.4 2018/04/26 +# +# Author: Fred N. van Kempen, +# +# Copyright 2018 Fred N. van Kempen. +# +# Redistribution and use in source and binary forms, with +# or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the entire +# above notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names +# of its contributors may be used to endorse or promote +# products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + [ "x${DEBUG}" = "xy" ] && TARGET=debug + if [ "x${DEV_BUILD}" = "xy" ]; then + TARGET="win-${TRAVIS_BUILD_NUMBER}_dev-x86" + elif [ "x${DEBUG}" = "xy" ]; then + TARGET="win-${TRAVIS_BUILD_NUMBER}_debug-x86" + else + TARGET="win-${TRAVIS_BUILD_NUMBER}-x86" + fi + echo "Building VARCem, build #${TRAVIS_BUILD_NUMBER} target ${TARGET}" + + cd src + + # We only need the first few characters of the commit ID. + export COMMIT=${TRAVIS_COMMIT::7} + + # Build the project. + make -f win/mingw/Makefile.MinGW BUILD=${TRAVIS_BUILD_NUMBER} + if [ $? != 0 ]; then + echo "Build failed, not uploading." + + exit 1 + fi + + echo "Build #${TRAVIS_BUILD_NUMBER} OK, packing up." + + zip -9 ../${TARGET}.zip *.exe + if [ $? != 0 ]; then + echo "ZIP failed, not uploading." + + exit 1 + fi + + exit 0 + +# End of file. diff --git a/.travis-deploy.sh b/.travis-deploy.sh new file mode 100644 index 0000000..62f9ecf --- /dev/null +++ b/.travis-deploy.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# +# VARCem Virtual ARchaeological Computer EMulator. +# An emulator of (mostly) x86-based PC systems and devices, +# using the ISA,EISA,VLB,MCA and PCI system buses, roughly +# spanning the era between 1981 and 1995. +# +# This file is part of the VARCem Project. +# +# Deployment script for the Travis CI remote builder service. +# +# Version: @(#).travis-deploy.sh 1.0.1 2018/11/04 +# +# Author: Fred N. van Kempen, +# +# Copyright 2018 Fred N. van Kempen. +# +# Redistribution and use in source and binary forms, with +# or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the entire +# above notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names +# of its contributors may be used to endorse or promote +# products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + # Ignore the cron builds. + [ "x${TRAVIS_EVENT_TYPE}" = "xcron" ] && exit 0 + + [ "x${DEBUG}" = "xy" ] && TARGET=debug + if [ "x${DEBUG}" = "xy" ]; then + TARGET="slirp-win-${TRAVIS_BUILD_NUMBER}_debug-x86" + BTYPE=debug + else + TARGET="slirp-win-${TRAVIS_BUILD_NUMBER}-x86" + BTYPE=regular + fi + + if [ ! -f ${TARGET}.zip ]; then + echo "Target file ${TARGET}.zip not found, giving up." + + exit 1 + fi + + # We only need the first few characters of the commit ID. + export COMMIT=${TRAVIS_COMMIT::7} + + echo "Uploading SLiRP build #${TRAVIS_BUILD_NUMBER} target ${TARGET}" + + curl -# -X POST \ + -F "type=${BTYPE}" \ + -F "build=${TRAVIS_BUILD_NUMBER}" \ + -F "id=${COMMIT}" \ + -F "notes=not available" \ + -F "file_name=@${TARGET}.zip" \ + ${SITE_URL} + + if [ $? != 0 ]; then + echo Upload failed. + exit 1 + fi + + exit 0 + +# End of file. diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c23b8d4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,91 @@ +# +# VARCem Virtual ARchaeological Computer EMulator. +# An emulator of (mostly) x86-based PC systems and devices, +# using the ISA,EISA,VLB,MCA and PCI system buses, roughly +# spanning the era between 1981 and 1995. +# +# This file is part of the VARCem Project. +# +# Project file for the Travis CI remote builder service. +# +# Version: @(#).travis.yml 1.0.1 2018/11/04 +# +# Author: Fred N. van Kempen, +# +# Copyright 2018 Fred N. van Kempen. +# +# Redistribution and use in source and binary forms, with +# or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the entire +# above notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names +# of its contributors may be used to endorse or promote +# products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +sudo: required +dist: trusty + +language: c + +env: + global: + - MAKEFLAGS="-j 4" + + matrix: + - PROG=libslirp CROSS=y DEBUG=n + - PROG=libslirp CROSS=y DEBUG=y + +before_install: + +script: + - chmod +x .travis-build.sh .travis-deploy.sh + - ./.travis-build.sh + +addons: + apt: + packages: + - binutils-mingw-w64-i686 + - gcc-mingw-w64-i686 + - binutils-mingw-w64-x86-64 + - gcc-mingw-w64-x86-64 + - gcc-mingw-w64 + - mingw-w64 + +notifications: + irc: + channels: + - "chat.freenode.net#varcem-dev" + - "chat.freenode.net#varcem" + template: + - "Build #%{build_number} for #%{commit} by %{author} in %{branch} %{result}." + - "Commit changes: %{commit_subject}" + - "Build details on %{build_url}" +# - "Details on %{build_url}, changes on %{compare_url}" + +after_success: + - ./.travis-deploy.sh + + +# End of file. diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt new file mode 100644 index 0000000..62cceba --- /dev/null +++ b/COPYRIGHT.txt @@ -0,0 +1,61 @@ +Slirp was written by Danny Gasparovski. +Copyright (c), 1995,1996 All Rights Reserved. + +Slirp is maintained by Kelly Price + +Slirp is free software; "free" as in you don't have to pay for it, and you +are free to do whatever you want with it. I do not accept any donations, +monetary or otherwise, for Slirp. Instead, I would ask you to pass this +potential donation to your favorite charity. In fact, I encourage +*everyone* who finds Slirp useful to make a small donation to their +favorite charity (for example, GreenPeace). This is not a requirement, but +a suggestion from someone who highly values the service they provide. + +The copyright terms and conditions: + +---BEGIN--- + + Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---END--- + +This basically means you can do anything you want with the software, except +1) call it your own, and 2) claim warranty on it. There is no warranty for +this software. None. Nada. If you lose a million dollars while using +Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***. + +If these conditions cannot be met due to legal restrictions (E.g. where it +is against the law to give out Software without warranty), you must cease +using the software and delete all copies you have. + +Slirp uses code that is copyrighted by the following people/organizations: + +Juha Pirkola. +Gregory M. Christy. +The Regents of the University of California. +Carnegie Mellon University. +The Australian National University. +RSA Data Security, Inc. + +Please read the top of each source file for the details on the various +copyrights. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8708be --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +Welcome to **VARCem**, the Virtual Archaeological Computer EMulator. + +This is the SLiRP Host Gateway module for VARCem. It is based on a much- +hacked variant of SLiRP, initially modified for use with the PCem emulator +by neozeed. + +As an external module, it can be used as needed. Configuration is handled +by the calling application (VARCem), and multiple instances can be run at +the same time. + +The SLiRP module can be considered a simple, virtual in-application IP +router with PAT (Port Address Translation) capabilities. Its "internal" leg +talks to the emulated network cards within the emulator, and the "external" +side use the host system's network stack to connect to the outside world, +using the host's IP address and translating back to the internal side. + + +Community and Support +--------------------- +Information, downloads, additional modules and such can be found on our +[Website](http://www.varcem.com/). Live support and general help can +also be found on our [IRC channel](https://kiwiirc.com/client/irc.freenode.net/?nick=VARCem_Guest|?#VARCem) + + +LEGAL +----- +"This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +It's free, it comes with all the sources, but it does not come with +any warranty whatsoever. You probably should not use this software to run +a business-critical piece of (old) software at work, for example. + +If there is a problem with the software, please open up a GIT issue so we +can work on it, and/or talk to us on the IRC channel. We cannot promise a +fix, but will try the best we can ! + + +BUILD STATUS +------------ +The auto-builds handled by Travis-CI are [![Build Status](https://travis-ci.org/VARCem/SLiRP.svg?branch=master)](https://travis-ci.org/VARCem/SLiRP) + +Last Updated: 11/04/2018 diff --git a/git b/git new file mode 100644 index 0000000..e69de29 diff --git a/src/arp.c b/src/arp.c new file mode 100644 index 0000000..4807f14 --- /dev/null +++ b/src/arp.c @@ -0,0 +1,75 @@ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "mbuf.h" +#include "if.h" +#include "arp.h" + + +void +arp_input(const uint8_t *pkt, int pkt_len) +{ + uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)]; + struct ethhdr *eh = (struct ethhdr *)pkt; + struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN); + struct ethhdr *reh = (struct ethhdr *)arp_reply; + struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN); +#ifdef USE_REDIR + struct ex_list *ex_ptr; +#endif + int ar_op; + + ar_op = ntohs(ah->ar_op); + switch (ar_op) { + case ARPOP_REQUEST: + if (! memcmp(ah->ar_tip, &special_addr, 3)) { + if (ah->ar_tip[3] == CTL_ROUTER || + ah->ar_tip[3] == CTL_SERVER) + goto arp_ok; +#ifdef USE_REDIR + for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_addr == ah->ar_tip[3]) + goto arp_ok; + } +#endif + return; + +arp_ok: + /* XXX: make an ARP request for the client address */ + memcpy(client_macaddr, eh->h_source, ETH_ALEN); + + /* ARP request for alias/dns mac address */ + memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); + memcpy(reh->h_source, special_macaddr, ETH_ALEN - 1); + reh->h_source[5] = ah->ar_tip[3]; + reh->h_proto = htons(ETH_P_ARP); + + rah->ar_hrd = htons(1); + rah->ar_pro = htons(ETH_P_IP); + rah->ar_hln = ETH_ALEN; + rah->ar_pln = 4; + rah->ar_op = htons(ARPOP_REPLY); + memcpy(rah->ar_sha, reh->h_source, ETH_ALEN); + memcpy(rah->ar_sip, ah->ar_tip, 4); + memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); + memcpy(rah->ar_tip, ah->ar_sip, 4); + slirp_output(arp_reply, sizeof(arp_reply)); + } + break; + + default: + break; + } +} diff --git a/src/arp.h b/src/arp.h new file mode 100644 index 0000000..25083f1 --- /dev/null +++ b/src/arp.h @@ -0,0 +1,42 @@ +#ifndef SLIRP_ARP_H +# define SLIRP_ARP_H + + +#define ETH_ALEN 6 +#define ETH_HLEN 14 + +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ + + +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ + + +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ +}; + +struct arphdr { + unsigned short ar_hrd; /* format of hardware address */ + unsigned short ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + unsigned short ar_op; /* ARP opcode (command) */ + + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +}; + + +extern void arp_input(const uint8_t *pkt, int pkt_len); + + +#endif /*SLIRP_ARP_H*/ diff --git a/src/bootp.c b/src/bootp.c new file mode 100644 index 0000000..00044ef --- /dev/null +++ b/src/bootp.c @@ -0,0 +1,359 @@ +/* + * QEMU BOOTP/DHCP server + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "mbuf.h" +#include "if.h" +#include "arp.h" +#include "ip.h" +#include "udp.h" +#include "bootp.h" + + +#define NB_ADDR 16 +#define START_ADDR 100 +#define LEASE_TIME (24 * 3600) + + +typedef struct { + uint8_t allocated; + uint8_t macaddr[ETH_ALEN]; +} BOOTPClient; + + +static BOOTPClient clients[NB_ADDR]; +static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; + + +static char * +pr_mac(const uint8_t *mac) +{ + static char buff[20]; + + sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + return(buff); +} + + +static BOOTPClient * +get_new_addr(const struct in_addr *ciaddr, struct in_addr *paddr) +{ + BOOTPClient *bc; + int i; + + for (i = 0; i < NB_ADDR; i++) { + bc = &clients[i]; + if (! bc->allocated) { + bc->allocated = 1; + paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR)); + return bc; + } + } + + return NULL; +} + + +static BOOTPClient * +find_addr(struct in_addr *paddr, const uint8_t *macaddr) +{ + BOOTPClient *bc; + int i; + + for (i = 0; i < NB_ADDR; i++) { + bc = &clients[i]; + + if (! memcmp(macaddr, bc->macaddr, 6)) { + bc = &clients[i]; + bc->allocated = 1; + paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR)); + return bc; + } + } + + return NULL; +} + + +static int +dhcp_decode(const uint8_t *buf, int size) +{ + const uint8_t *p, *p_end; + int msg_type, len, tag; + + msg_type = 0; + + p = buf; + p_end = buf + size; + if (size < 5) + return(msg_type); + + if (memcmp(p, rfc1533_cookie, 4) != 0) + return(msg_type); + + p += 4; + while (p < p_end) { + tag = p[0]; + + if (tag == RFC1533_PAD) { + p++; + } else if (tag == RFC1533_END) { + break; + } else { + p++; + if (p >= p_end) + break; + len = *p++; + + switch(tag) { + case RFC2132_MSG_TYPE: + if (len >= 1) + msg_type = p[0]; + break; + + default: + break; + } + p += len; + } + } + + return msg_type; +} + + +static void +bootp_reply(struct bootp_t *bp) +{ + BOOTPClient *bc; + struct SLIRPmbuf *m; + struct bootp_t *rbp; + struct sockaddr_in addr, saddr, daddr; + struct in_addr xaddr; + int msg_type, val; + uint8_t *q; + + /* Decode the Vendor area to guesstimate the protocol and message type. */ + msg_type = dhcp_decode(bp->bp_vend, DHCP_OPT_LEN); + + if (msg_type == 0) { + /* Force reply for old BOOTP clients */ + msg_type = BOOTPREQUEST; + lprint("BOOTP: BOOTP REQUEST from %s\n", pr_mac(bp->bp_hwaddr)); + } else if (msg_type == DHCPDISCOVER) { + lprint("BOOTP: DHCP DISCOVER from %s\n", pr_mac(bp->bp_hwaddr)); + } else if (msg_type == DHCPREQUEST) { + lprint("BOOTP: DHCP REQUEST from %s\n", pr_mac(bp->bp_hwaddr)); + } else { + lprint("BOOTP: invalid message %d from %s\n", pr_mac(bp->bp_hwaddr)); + return; + } + + /* Make sure we support this topology. */ + if (bp->bp_htype != 1 || bp->bp_hlen != ETH_ALEN) { + lprint("BOOTP: unsupported topology %d or hlen %d from %s\n", + bp->bp_htype, bp->bp_hlen, pr_mac(bp->bp_hwaddr)); + return; + } + + /* Save the client MAC address. */ + memcpy(client_macaddr, bp->bp_hwaddr, bp->bp_hlen); + + /* Reset client IP address. */ + memset(&addr, 0x00, sizeof(addr)); + addr.sin_port = htons(BOOTP_CLIENT); + + /* Create return packet. */ + if ((m = m_get()) == NULL) { + lprint("BOOTP: out of memory for reply to %s\n", + pr_mac(bp->bp_hwaddr)); + return; + } + m->m_data += if_maxlinkhdr; + rbp = (struct bootp_t *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + memset(rbp, 0x00, sizeof(struct bootp_t)); + + switch(msg_type) { + case BOOTPREQUEST: + /* + * If client already has an address, see if we can + * re-assign that to it. Otherwise, allocate a new + * address for the client. + */ + bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr); + if (! bc) + bc = get_new_addr(&bp->bp_ciaddr, &daddr.sin_addr); + if (! bc) { + lprint("BOOTP: out of slots for %s\n", + pr_mac(bp->bp_hwaddr)); + return; + } + memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen); + addr.sin_addr.s_addr = 0xffffffff; + break; + + case DHCPDISCOVER: +new_addr: + bc = get_new_addr(&bp->bp_ciaddr, &daddr.sin_addr); + if (! bc) { + lprint("DHCP: out of slots for %s\n", + pr_mac(bp->bp_hwaddr)); + return; + } + memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen); + break; + + default: + /* Find the client's MAC address. */ + bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr); + if (! bc) { + /* + * If client not found, could be a Windows + * machine trying to keep its current (but + * expired) address. FIXME: wrong!! --FvK + */ + goto new_addr; + } + break; + } + + /* Set up my IP address. */ + saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_SERVER); + saddr.sin_port = htons(BOOTP_SERVER); + + /* Target address is either broadcast, or the client IP address. */ + if (addr.sin_addr.s_addr == 0) + addr.sin_addr.s_addr = daddr.sin_addr.s_addr; + + /* Set up the reply packet. */ + rbp->bp_op = BOOTP_REPLY; + rbp->bp_xid = bp->bp_xid; + rbp->bp_htype = bp->bp_htype; + rbp->bp_hlen = bp->bp_hlen; + memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, bp->bp_hlen); + + rbp->bp_ciaddr = bp->bp_ciaddr; /* client current IP address */ + rbp->bp_yiaddr = daddr.sin_addr; /* client new IP address */ + rbp->bp_siaddr = saddr.sin_addr; /* server IP address */ + + /* Set up the RFC1533 cookie to indicate standard Vendor Extensions. */ + q = rbp->bp_vend; + memcpy(q, rfc1533_cookie, 4); + q += 4; + + if (msg_type == DHCPDISCOVER) { + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPOFFER; + } + + if (msg_type == DHCPREQUEST) { + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPACK; + } + + if (msg_type == DHCPDISCOVER || msg_type == DHCPREQUEST) { + *q++ = RFC2132_SRV_ID; + *q++ = 4; + memcpy(q, &saddr.sin_addr, 4); + q += 4; + + *q++ = RFC2132_LEASE_TIME; + *q++ = 4; + val = htonl(LEASE_TIME); + memcpy(q, &val, 4); + q += 4; + } + + if (msg_type == BOOTPREQUEST || msg_type == DHCPDISCOVER || msg_type == DHCPREQUEST) { + *q++ = RFC1533_NETMASK; + *q++ = 4; + *q++ = 0xff; /* FIXME: wrong, but works for now --FvK */ + *q++ = 0xff; + *q++ = 0xff; + *q++ = 0x00; + + *q++ = RFC1533_GATEWAY; + *q++ = 4; + xaddr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ROUTER); + memcpy(q, &xaddr, 4); + q += 4; + + *q++ = RFC1533_DNS; + *q++ = 4; + xaddr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_SERVER); + memcpy(q, &xaddr, 4); + q += 4; + + if (slirp_hostname[0]) { + val = strlen(slirp_hostname); + *q++ = RFC1533_HOSTNAME; + *q++ = val; + memcpy(q, slirp_hostname, val); + q += val; + } + + /* End of option list. */ + *q++ = RFC1533_END; + } + + m->m_len = sizeof(struct bootp_t) - + sizeof(struct ip) - sizeof(struct udphdr); +lprint("BOOTP: sending reply=%d to %s\n", m->m_len, inet_ntoa(addr.sin_addr)); + udp_output2(NULL, m, &saddr, &addr, IPTOS_LOWDELAY); +} + + +void +bootp_input(struct SLIRPmbuf *m) +{ + struct bootp_t *bp = mtod(m, struct bootp_t *); + + switch(bp->bp_op) { + case BOOTP_REQUEST: + bootp_reply(bp); + break; + + default: + lprint("BOOTP: invalid opcode %d (ignored)\n", bp->bp_op); + break; + } +} diff --git a/src/bootp.h b/src/bootp.h new file mode 100644 index 0000000..2cc69a1 --- /dev/null +++ b/src/bootp.h @@ -0,0 +1,122 @@ +/* bootp/dhcp defines */ + +#define BOOTP_SERVER 67 +#define BOOTP_CLIENT 68 + +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +#define RFC1533_COOKIE 99, 130, 83, 99 +#define RFC1533_PAD 0 +#define RFC1533_NETMASK 1 +#define RFC1533_TIMEOFFSET 2 +#define RFC1533_GATEWAY 3 +#define RFC1533_TIMESERVER 4 +#define RFC1533_IEN116NS 5 +#define RFC1533_DNS 6 +#define RFC1533_LOGSERVER 7 +#define RFC1533_COOKIESERVER 8 +#define RFC1533_LPRSERVER 9 +#define RFC1533_IMPRESSSERVER 10 +#define RFC1533_RESOURCESERVER 11 +#define RFC1533_HOSTNAME 12 +#define RFC1533_BOOTFILESIZE 13 +#define RFC1533_MERITDUMPFILE 14 +#define RFC1533_DOMAINNAME 15 +#define RFC1533_SWAPSERVER 16 +#define RFC1533_ROOTPATH 17 +#define RFC1533_EXTENSIONPATH 18 +#define RFC1533_IPFORWARDING 19 +#define RFC1533_IPSOURCEROUTING 20 +#define RFC1533_IPPOLICYFILTER 21 +#define RFC1533_IPMAXREASSEMBLY 22 +#define RFC1533_IPTTL 23 +#define RFC1533_IPMTU 24 +#define RFC1533_IPMTUPLATEAU 25 +#define RFC1533_INTMTU 26 +#define RFC1533_INTLOCALSUBNETS 27 +#define RFC1533_INTBROADCAST 28 +#define RFC1533_INTICMPDISCOVER 29 +#define RFC1533_INTICMPRESPOND 30 +#define RFC1533_INTROUTEDISCOVER 31 +#define RFC1533_INTROUTESOLICIT 32 +#define RFC1533_INTSTATICROUTES 33 +#define RFC1533_LLTRAILERENCAP 34 +#define RFC1533_LLARPCACHETMO 35 +#define RFC1533_LLETHERNETENCAP 36 +#define RFC1533_TCPTTL 37 +#define RFC1533_TCPKEEPALIVETMO 38 +#define RFC1533_TCPKEEPALIVEGB 39 +#define RFC1533_NISDOMAIN 40 +#define RFC1533_NISSERVER 41 +#define RFC1533_NTPSERVER 42 +#define RFC1533_VENDOR 43 +#define RFC1533_NBNS 44 +#define RFC1533_NBDD 45 +#define RFC1533_NBNT 46 +#define RFC1533_NBSCOPE 47 +#define RFC1533_XFS 48 +#define RFC1533_XDM 49 + +#define RFC2132_REQ_ADDR 50 +#define RFC2132_LEASE_TIME 51 +#define RFC2132_MSG_TYPE 53 +#define RFC2132_SRV_ID 54 +#define RFC2132_PARAM_LIST 55 +#define RFC2132_MAX_SIZE 57 +#define RFC2132_RENEWAL_TIME 58 +#define RFC2132_REBIND_TIME 59 + +#define BOOTPREQUEST 0 +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPACK 5 + +#define RFC1533_VENDOR_MAJOR 0 +#define RFC1533_VENDOR_MINOR 0 + +#define RFC1533_VENDOR_MAGIC 128 +#define RFC1533_VENDOR_ADDPARM 129 +#define RFC1533_VENDOR_ETHDEV 130 +#define RFC1533_VENDOR_HOWTO 132 +#define RFC1533_VENDOR_MNUOPTS 160 +#define RFC1533_VENDOR_SELECTION 176 +#define RFC1533_VENDOR_MOTD 184 +#define RFC1533_VENDOR_NUMOFMOTD 8 +#define RFC1533_VENDOR_IMG 192 +#define RFC1533_VENDOR_NUMOFIMG 16 + +#define RFC1533_END 255 +#define BOOTP_VENDOR_LEN 64 +#define DHCP_OPT_LEN 312 + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct bootp_t { + struct ip ip; + struct udphdr udp; + uint8_t bp_op; + uint8_t bp_htype; + uint8_t bp_hlen; + uint8_t bp_hops; + uint32_t bp_xid; + uint16_t bp_secs; + uint16_t unused; + struct in_addr bp_ciaddr; + struct in_addr bp_yiaddr; + struct in_addr bp_siaddr; + struct in_addr bp_giaddr; + uint8_t bp_hwaddr[16]; + uint8_t bp_sname[64]; + uint8_t bp_file[128]; + uint8_t bp_vend[DHCP_OPT_LEN]; +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +void bootp_input(struct SLIRPmbuf *m); diff --git a/src/cksum.c b/src/cksum.c new file mode 100644 index 0000000..51084ef --- /dev/null +++ b/src/cksum.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1988, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 + * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp + */ +#include +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "mbuf.h" + + +/* + * Checksum routine for Internet Protocol family headers (Portable Version). + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + * + * XXX Since we will never span more than 1 SLIRPmbuf, we can optimise this + */ + +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} + + +int cksum(struct SLIRPmbuf *m, int len) +{ + register uint16_t *w; + register int sum = 0; + register int mlen = 0; + int byte_swapped = 0; + + union { + uint8_t c[2]; + uint16_t s; + } s_util; + union { + uint16_t s[2]; + uint32_t l; + } l_util; + + if (m->m_len == 0) + goto cont; + w = mtod(m, uint16_t *); + + mlen = m->m_len; + + if (len < mlen) + mlen = len; + len -= mlen; + /* + * Force to even boundary. + */ + if ((1 & (intptr_t) w) && (mlen > 0)) { + REDUCE; + sum <<= 8; + s_util.c[0] = *(uint8_t *)w; + w = (uint16_t *)((int8_t *)w + 1); + mlen--; + byte_swapped = 1; + } + /* + * Unroll the loop to make overhead from + * branches &c small. + */ + while ((mlen -= 32) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; + sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; + sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; + w += 16; + } + mlen += 32; + while ((mlen -= 8) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + w += 4; + } + mlen += 8; + if (mlen == 0 && byte_swapped == 0) + goto cont; + REDUCE; + while ((mlen -= 2) >= 0) { + sum += *w++; + } + + if (byte_swapped) { + REDUCE; + sum <<= 8; + byte_swapped = 0; + if (mlen == -1) { + s_util.c[1] = *(uint8_t *)w; + sum += s_util.s; + mlen = 0; + } else + + mlen = -1; + } else if (mlen == -1) + s_util.c[0] = *(uint8_t *)w; + +cont: +#ifdef _DEBUG + if (len) { + DEBUG_ERROR((dfd, "cksum: out of data\n")); + DEBUG_ERROR((dfd, " len = %d\n", len)); + } +#endif + if (mlen == -1) { + /* The last SLIRPmbuf has odd # of bytes. Follow the + standard (the odd byte may be shifted left by 8 bits + or not as determined by endian-ness of the machine) */ + s_util.c[1] = 0; + sum += s_util.s; + } + REDUCE; + return (~sum & 0xffff); +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..af6ec27 --- /dev/null +++ b/src/config.h @@ -0,0 +1,71 @@ +/* Define to sizeof(char *) */ +#ifdef SIZEOF_VOID_P +# define SIZEOF_CHAR_P SIZEOF_VOID_P +#else +# define SIZEOF_CHAR_P 8 /*FIXME: sizeof() does not work in cpp!*/ +#endif + +#define HAVE_MEMMOVE /* Define if you have memmove() */ +#define HAVE_STRERROR /* Define if you have strerror() */ + + +#ifdef _WIN32 +# define DECLARE_IOVEC +typedef unsigned int u_int; +typedef char *SLIRPcaddr_t; +typedef int socklen_t; +typedef unsigned long ioctlsockopt_t; + +# define USE_FIONBIO 1 +# ifndef EWOULDBLOCK +# define EWOULDBLOCK WSAEWOULDBLOCK +# endif +# ifndef EINPROGRESS +# define EINPROGRESS WSAEINPROGRESS +# endif +# ifndef ENOTCONN +# define ENOTCONN WSAENOTCONN +# endif +# ifndef EHOSTUNREACH +# define EHOSTUNREACH WSAEHOSTUNREACH +# endif +# ifndef ENETUNREACH +# define ENETUNREACH WSAENETUNREACH +# endif +# ifndef ECONNREFUSED +# define ECONNREFUSED WSAECONNREFUSED +# endif + +# define udp_read_completion slirp_udp_read_completion +# define write_udp slirp_write_udp +# define init_udp slirp_init_udp +# define final_udp slirp_final_udp +#else +# define HAVE_INET_ATON +typedef char *SLIRPcaddr_t; +typedef int ioctlsockopt_t; +# define ioctlsocket ioctl +# define closesocket(s) close(s) +# define O_BINARY 0 +#endif + +#ifndef HAVE_MEMMOVE +# define memmove(x, y, z) bcopy(y, x, z) +#endif + +#ifdef GETTIMEOFDAY_ONE_ARG +# define gettimeofday(x, y) gettimeofday(x) +#endif + + +#if defined(__GNUC__) +#define PACKED__ __attribute__ ((packed)) +#elif defined(__sgi) +#define PRAGMA_PACK_SUPPORTED 1 +#define PACK_END 0 +#define PACKED__ +#elif defined(_MSC_VER) +#define PACKED__ +#else +#error "Packed attribute or pragma shall be supported" +#endif diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..7093097 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * Portions copyright (c) 2000 Kelly Price. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#include +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h + + +static log_func_t log_func = NULL; +FILE *dfd = NULL; +#ifdef _DEBUG +int dostats = 1; +#else +int dostats = 0; +#endif +int dbglvl = 0; + + +void +debug_init(char *file, int dbg) +{ + /* Close the old debugging file */ + if (dfd) + fclose(dfd); + + /* Reset logger function. */ + log_func = NULL; + + dfd = fopen(file, "w"); + if (dfd != NULL) { + dbglvl = dbg; + + fprintf(dfd, "Debugging Started level %i.\n", dbglvl); + fflush(dfd); + } +} + + +void +lprint(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (log_func != NULL) + (*log_func)(NULL, fmt, args); + + va_end(args); +} + + +/* Dump a packet in the same format as tcpdump -x */ +#ifdef _DEBUG +void +dump_packet(void *dat, int n) +{ + u_char *pptr = (u_char *)dat; + int j, k; + + n /= 16; + n++; + DEBUG_MISC((dfd, "PACKET DUMPED:\n")); + for (j = 0; j < n; j++) { + for(k = 0; k < 6; k++) + DEBUG_MISC((dfd, "%02x ", *pptr++)); + DEBUG_MISC((dfd, "\n")); + fflush(dfd); + } +} +#endif + + +/* API: set logging output function to caller. */ +void +slirp_debug(log_func_t func) +{ + log_func = func; + + /* Feedback to indicate logging is set up. */ + lprint("SLiRP: debug level %i\n", dbglvl); +} diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..f0ec2ed --- /dev/null +++ b/src/debug.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifndef DEBUG_H +# define DEBUG_H + + +#define DBG_CALL 0x01 +#define DBG_MISC 0x02 +#define DBG_ERROR 0x04 +#define DEBUG_DEFAULT DBG_CALL|DBG_MISC|DBG_ERROR + + +#ifdef _DEBUG +# define DEBUG_CALL(x) \ + if (dbglvl & DBG_CALL) { \ + fprintf(dfd, "%s...\n", x); \ + fflush(dfd); \ + } +# define DEBUG_ARG(x, y) \ + if (dbglvl & DBG_CALL) { \ + fputc(' ', dfd); \ + fprintf(dfd, x, y); \ + fputc('\n', dfd); \ + fflush(dfd); \ + } +# define DEBUG_ARGS(x) \ + if (dbglvl & DBG_CALL) { \ + fprintf x ; \ + fflush(dfd); \ + } +# define DEBUG_MISC(x) \ + if (dbglvl & DBG_MISC) { \ + fprintf x ; \ + fflush(dfd); \ + } +# define DEBUG_ERROR(x) \ + if (dbglvl & DBG_ERROR) { \ + fprintf x ; \ + fflush(dfd); \ + } +#else +# define DEBUG_CALL(x) +# define DEBUG_ARG(x, y) +# define DEBUG_ARGS(x) +# define DEBUG_MISC(x) +# define DEBUG_ERROR(x) +#endif + + +extern FILE *dfd; +extern int dostats; +extern int dbglvl; + + +extern void debug_init(char *, int); +extern void lprint(const char *fmt, ...); +#ifdef _DEBUG +extern void dump_packet(void *dat, int n); +#endif + +#ifndef _MSC_VER +extern char *strerror(int); +#endif + + +#endif /*DEBUG_H*/ diff --git a/src/host.h b/src/host.h new file mode 100644 index 0000000..a7a1dd5 --- /dev/null +++ b/src/host.h @@ -0,0 +1,14 @@ +#ifndef SLIRP_HOST_H +# define SLIRP_HOST_H + + +extern int host_get_info(struct in_addr *, char *, char *); +extern int host_init(void); +extern void host_close(void); + +#ifdef _WIN32 +extern int inet_aton(const char *, struct in_addr *); +#endif + + +#endif /*SLIRP_HOST_H*/ diff --git a/src/icmp_var.h b/src/icmp_var.h new file mode 100644 index 0000000..9af222f --- /dev/null +++ b/src/icmp_var.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93 + * icmp_var.h,v 1.4 1995/02/16 00:27:40 wollman Exp + */ + +#ifndef _NETINET_ICMP_VAR_H_ +#define _NETINET_ICMP_VAR_H_ + +/* + * Variables related to this implementation + * of the internet control message protocol. + */ +struct icmpstat { +/* statistics related to input messages processed */ + u_long icps_received; /* #ICMP packets received */ + u_long icps_tooshort; /* packet < ICMP_MINLEN */ + u_long icps_checksum; /* bad checksum */ + u_long icps_notsupp; /* #ICMP packets not supported */ + u_long icps_badtype; /* #with bad type feild */ + u_long icps_reflect; /* number of responses */ +}; + +/* + * Names for ICMP sysctl objects + */ +#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */ +#define ICMPCTL_STATS 2 /* statistics (read-only) */ +#define ICMPCTL_MAXID 3 + +#define ICMPCTL_NAMES { \ + { 0, 0 }, \ + { "maskrepl", CTLTYPE_INT }, \ + { "stats", CTLTYPE_STRUCT }, \ +} + +extern struct icmpstat icmpstat; + +#endif diff --git a/src/if.c b/src/if.c new file mode 100644 index 0000000..1a74e56 --- /dev/null +++ b/src/if.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" + + +#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm)) + + +int if_mtu, if_mru; +int if_maxlinkhdr; +int if_queued = 0; /* Number of packets queued so far */ + + +static struct SLIRPmbuf if_fastq; /* fast queue (for interactive data) */ +static struct SLIRPmbuf if_batchq; /* queue for non-interactive data */ +static struct SLIRPmbuf *next_m; /* Pointer to next SLIRPmbuf to output */ + + +static void +ifs_insque(struct SLIRPmbuf *ifm, struct SLIRPmbuf *ifmhead) +{ + ifm->ifs_next = ifmhead->ifs_next; + ifmhead->ifs_next = ifm; + ifm->ifs_prev = ifmhead; + ifm->ifs_next->ifs_prev = ifm; +} + + +static void +ifs_remque(struct SLIRPmbuf *ifm) +{ + ifm->ifs_prev->ifs_next = ifm->ifs_next; + ifm->ifs_next->ifs_prev = ifm->ifs_prev; +} + + +void +if_init(slirp_t *slirp) +{ + if_mtu = 1500; + if_mru = 1500; + + if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq; + if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq; + next_m = &if_batchq; +} + + +/* + * if_output: Queue packet into an output queue. + * + * There are 2 output queues, if_fastq and if_batchq. + * Each output queue is a doubly linked list of double linked lists + * of SLIRPmbufs, each list belonging to one "session" (socket). This + * way, we can output packets fairly by sending one packet from each + * session, instead of all the packets from one session, then all packets + * from the next session, etc. Packets on the if_fastq get absolute + * priority, but if one session hogs the link, it gets "downgraded" + * to the batchq until it runs out of packets, then it'll return + * to the fastq (eg. if the user does an ls -alR in a telnet session, + * it'll temporarily get downgraded to the batchq) + */ +void +if_output(struct SLIRPsocket *so, struct SLIRPmbuf *ifm) +{ + struct SLIRPmbuf *ifq; + int on_fastq = 1; + + DEBUG_CALL("if_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("ifm = %lx", (long)ifm); + + /* + * First remove the SLIRPmbuf from m_usedlist, + * since we're gonna use m_next and m_prev ourselves + * XXX Shouldn't need this, gotta change dtom() etc. + */ + if (ifm->m_flags & M_USEDLIST) { + remque(ifm); + ifm->m_flags &= ~M_USEDLIST; + } + + /* + * See if there's already a batchq list for this session. + * This can include an interactive session, which should go on fastq, + * but gets too greedy... hence it'll be downgraded from fastq to batchq. + * We mustn't put this packet back on the fastq (or we'll send it out of order) + * XXX add cache here? + */ + for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) { + if (so == ifq->ifq_so) { + /* A match! */ + ifm->ifq_so = so; + ifs_insque(ifm, ifq->ifs_prev); + goto diddit; + } + } + + /* No match, check which queue to put it on */ + if (so && (so->so_iptos & IPTOS_LOWDELAY)) { + ifq = if_fastq.ifq_prev; + on_fastq = 1; + + /* + * Check if this packet is a part of the last + * packet's session + */ + if (ifq->ifq_so == so) { + ifm->ifq_so = so; + ifs_insque(ifm, ifq->ifs_prev); + goto diddit; + } + } else + ifq = if_batchq.ifq_prev; + + /* Create a new doubly linked list for this session */ + ifm->ifq_so = so; + ifs_init(ifm); + insque(ifm, ifq); + +diddit: + if_queued++; + + if (so) { + /* Update *_queued */ + so->so_queued++; + so->so_nqueued++; + + /* + * Check if the interactive session should be downgraded to + * the batchq. A session is downgraded if it has queued 6 + * packets without pausing, and at least 3 of those packets + * have been sent over the link + * (XXX These are arbitrary numbers, probably not optimal..) + */ + if (on_fastq && ((so->so_nqueued >= 6) && + (so->so_nqueued - so->so_queued) >= 3)) { + /* Remove from current queue... */ + remque(ifm->ifs_next); + + /* ...And insert in the new. That'll teach ya! */ + insque(ifm->ifs_next, &if_batchq); + } + } + +#ifndef FULL_BOLT + /* This prevents us from malloc()ing too many SLIRPmbufs. */ + if (link_up) { + /* if_start will check towrite */ + if_start(); + } +#endif +} + + +/* + * Send a packet + * + * We choose a packet based on it's position in the output queues; + * If there are packets on the fastq, they are sent FIFO, before + * everything else. Otherwise we choose the first packet from the + * batchq and send it. the next packet chosen will be from the session + * after this one, then the session after that one, and so on.. So, + * for example, if there are 3 ftp session's fighting for bandwidth, + * one packet will be sent from the first session, then one packet + * from the second session, then one packet from the third, then back + * to the first, etc. etc. + */ +void +if_start(void) +{ + struct SLIRPmbuf *ifm, *ifqt; + + DEBUG_CALL("if_start"); + + if (if_queued == 0) + return; /* Nothing to do */ + +again: + /* check if we can really output */ + if (! slirp_can_output()) + return; + + /* + * See which queue to get next packet from + * If there's something in the fastq, select it immediately + */ + if (if_fastq.ifq_next != &if_fastq) { + ifm = if_fastq.ifq_next; + } else { + /* Nothing on fastq, see if next_m is valid */ + if (next_m != &if_batchq) + ifm = next_m; + else + ifm = if_batchq.ifq_next; + + /* Set which packet to send on next iteration */ + next_m = ifm->ifq_next; + } + + /* Remove it from the queue */ + ifqt = ifm->ifq_prev; + remque(ifm); + if_queued--; + + /* If there are more packets for this session, re-queue them */ + if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) { + insque(ifm->ifs_next, ifqt); + ifs_remque(ifm); + } + + /* Update so_queued */ + if (ifm->ifq_so) { + if (--ifm->ifq_so->so_queued == 0) { + /* If there's no more queued, reset nqueued */ + ifm->ifq_so->so_nqueued = 0; + } + } + + /* Encapsulate the packet for sending */ + if_encap((uint8_t*)ifm->m_data, ifm->m_len); + + m_free(ifm); + + if (if_queued) + goto again; +} diff --git a/src/if.h b/src/if.h new file mode 100644 index 0000000..1a59e64 --- /dev/null +++ b/src/if.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifndef SLIRP_IF_H +# define SLIRP_IF_H + + +/* Needed for FreeBSD */ +#undef if_mtu +extern int if_mtu; +extern int if_mru; /* MTU and MRU */ +extern int if_maxlinkhdr; +extern int if_queued; /* Number of packets queued so far */ + + +/* Interface statistics */ +struct slirp_ifstats { + u_int out_pkts; /* Output packets */ + u_int out_bytes; /* Output bytes */ + u_int out_errpkts; /* Output Error Packets */ + u_int out_errbytes; /* Output Error Bytes */ + u_int in_pkts; /* Input packets */ + u_int in_bytes; /* Input bytes */ + u_int in_errpkts; /* Input Error Packets */ + u_int in_errbytes; /* Input Error Bytes */ + + u_int bytes_saved; /* Number of bytes that compression "saved" */ + /* ie: #bytes not sent over the link + * because of compression */ + + u_int in_mbad; /* Bad incoming packets */ +}; + + +extern void if_init(slirp_t *); +extern void if_start(void); +extern void if_output(struct SLIRPsocket *, struct SLIRPmbuf *); +extern void if_encap(const uint8_t *ip_data, int ip_data_len); + + +#endif /*SLIRP_IF_H*/ diff --git a/src/ip.h b/src/ip.h new file mode 100644 index 0000000..8ea1768 --- /dev/null +++ b/src/ip.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 + * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp + */ +#ifndef SLIRP_IP_H +# define SLIRP_IP_H + + +#ifdef WORDS_BIGENDIAN +# ifndef NTOHL +# define NTOHL(d) +# endif +# ifndef NTOHS +# define NTOHS(d) +# endif +# ifndef HTONL +# define HTONL(d) +# endif +# ifndef HTONS +# define HTONS(d) +# endif +#else +# ifndef NTOHL +# define NTOHL(d) ((d) = ntohl((d))) +# endif +# ifndef NTOHS +# define NTOHS(d) ((d) = ntohs((uint16_t)(d))) +# endif +# ifndef HTONL +# define HTONL(d) ((d) = htonl((d))) +# endif +# ifndef HTONS +# define HTONS(d) ((d) = htons((uint16_t)(d))) +# endif +#endif + + +typedef uint32_t n_long; /* long as received from the net */ + + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ +#define IPVERSION 4 + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif + +/* + * Structure of an internet header, naked of options. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif +struct ip { +#ifdef WORDS_BIGENDIAN + uint8_t ip_v:4, /* version */ + ip_hl:4; /* header length */ +#else + uint8_t ip_hl:4, /* header length */ + ip_v:4; /* version */ +#endif + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* don't fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +} PACKED__; +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) //WAS 0 +#endif + +#define IP_MAXPACKET 65535 /* maximum packet size */ + +/* + * Definitions for IP type of service (ip_tos) + */ +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 + +/* + * Definitions for options. + */ +#define IPOPT_COPIED(o) ((o)&0x80) +#define IPOPT_CLASS(o) ((o)&0x60) +#define IPOPT_NUMBER(o) ((o)&0x1f) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_DEBMEAS 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_EOL 0 /* end of option list */ +#define IPOPT_NOP 1 /* no operation */ + +#define IPOPT_RR 7 /* record packet route */ +#define IPOPT_TS 68 /* timestamp */ +#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ +#define IPOPT_LSRR 131 /* loose source route */ +#define IPOPT_SATID 136 /* satnet id */ +#define IPOPT_SSRR 137 /* strict source route */ + +/* + * Offsets to fields in options other than EOL and NOP. + */ +#define IPOPT_OPTVAL 0 /* option ID */ +#define IPOPT_OLEN 1 /* option length */ +#define IPOPT_OFFSET 2 /* offset within option */ +#define IPOPT_MINOFF 4 /* min value of above */ + +/* + * Time stamp option structure. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif +struct ip_timestamp { + uint8_t ipt_code; /* IPOPT_TS */ + uint8_t ipt_len; /* size of structure (variable) */ + uint8_t ipt_ptr; /* index of current entry */ +#ifdef WORDS_BIGENDIAN + uint8_t ipt_oflw:4, /* overflow counter */ + ipt_flg:4; /* flags, see below */ +#else + uint8_t ipt_flg:4, /* flags, see below */ + ipt_oflw:4; /* overflow counter */ +#endif + union ipt_timestamp { + n_long ipt_time[1]; + struct ipt_ta { + struct in_addr ipt_addr; + n_long ipt_time; + } ipt_ta[1]; + } ipt_timestamp; +} PACKED__; +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +/* flag bits for ipt_flg */ +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +/* bits for security (not byte swapped) */ +#define IPOPT_SECUR_UNCLASS 0x0000 +#define IPOPT_SECUR_CONFID 0xf135 +#define IPOPT_SECUR_EFTO 0x789a +#define IPOPT_SECUR_MMMM 0xbc4d +#define IPOPT_SECUR_RESTR 0xaf13 +#define IPOPT_SECUR_SECRET 0xd788 +#define IPOPT_SECUR_TOPSECRET 0x6bc5 + +/* + * Internet implementation parameters. + */ +#define MAXTTL 255 /* maximum time to live (seconds) */ +#define IPDEFTTL 64 /* default ttl, from RFC 1340 */ +#define IPFRAGTTL 60 /* time to live for frags, slowhz */ +#define IPTTLDEC 1 /* subtracted when forwarding */ + +#define IP_MSS 576 /* default maximum segment size */ + +#ifdef HAVE_SYS_TYPES32_H /* Overcome some Solaris 2.x junk */ +#include +#else +#if SIZEOF_CHAR_P == 4 +typedef SLIRPcaddr_t caddr32_t; +#else +typedef uint32_t caddr32_t; +#endif +#endif + +#ifdef __amd64__ +typedef uintptr_t ipqp_32; +typedef uintptr_t ipasfragp_32; +#else +#if SIZEOF_CHAR_P == 4 +typedef struct ipq *ipqp_32; +typedef struct ipasfrag *ipasfragp_32; +#else +typedef caddr32_t ipqp_32; +typedef caddr32_t ipasfragp_32; +#endif +#endif + +/* + * Overlay for ip header used by other protocols (tcp, udp). + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif +struct ipovly { +#ifdef __amd64__ + uintptr_t ih_next, ih_prev; /* for protocol sequence q's */ +#else + caddr32_t ih_next, ih_prev; /* for protocol sequence q's */ +#endif + uint8_t ih_x1; /* (unused) */ + uint8_t ih_pr; /* protocol */ + uint16_t ih_len; /* protocol length */ + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ +} PACKED__; +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +#if defined(_MSC_VER) +#pragma pack(pop) +#endif + +/* + * Ip reassembly queue structure. Each fragment + * being reassembled is attached to one of these structures. + * They are timed out after ipq_ttl drops to 0, and may also + * be reclaimed if memory becomes tight. + * size 28 bytes + */ +struct ipq { +#ifdef __amd64__ + uintptr_t next,prev; /* to other reass headers */ +#else + ipqp_32 next,prev; /* to other reass headers */ +#endif + uint8_t ipq_ttl; /* time for reass q to live */ + uint8_t ipq_p; /* protocol of this fragment */ + uint16_t ipq_id; /* sequence id for reassembly */ + ipasfragp_32 ipq_next,ipq_prev; + /* to ip headers of fragments */ + struct in_addr ipq_src,ipq_dst; +}; + +/* + * Ip header, when holding a fragment. + * + * Note: ipf_next must be at same offset as ipq_next above + */ +struct ipasfrag { +#ifdef WORDS_BIGENDIAN + uint8_t ip_v:4, + ip_hl:4; +#else + uint8_t ip_hl:4, + ip_v:4; +#endif + /* BUG : u_int changed to u_int8_t. + * sizeof(u_int)==4 on linux 2.0 + */ + uint8_t ipf_mff; /* XXX overlays ip_tos: use low bit + * to avoid destroying tos (PPPDTRuu); + * copied from (ip_off&IP_MF) */ + uint16_t ip_len; + uint16_t ip_id; + uint16_t ip_off; + uint8_t ip_ttl; + uint8_t ip_p; + uint16_t ip_sum; + ipasfragp_32 ipf_next; /* next fragment */ + ipasfragp_32 ipf_prev; /* previous fragment */ +}; + +/* + * Structure stored in mbuf in inpcb.ip_options + * and passed to ip_output when ip options are in use. + * The actual length of the options (including ipopt_dst) + * is in m_len. + */ +#define MAX_IPOPTLEN 40 + +struct ipoption { + struct in_addr ipopt_dst; /* first-hop dst if source routed */ + int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */ +}; + +/* + * Structure attached to inpcb.ip_moptions and + * passed to ip_output when IP multicast options are in use. + */ + +struct ipstat { + uint32_t ips_total; /* total packets received */ + uint32_t ips_badsum; /* checksum bad */ + uint32_t ips_tooshort; /* packet too short */ + uint32_t ips_toosmall; /* not enough data */ + uint32_t ips_badhlen; /* ip header length < data size */ + uint32_t ips_badlen; /* ip length < ip header length */ + uint32_t ips_fragments; /* fragments received */ + uint32_t ips_fragdropped; /* frags dropped (dups, out of space) */ + uint32_t ips_fragtimeout; /* fragments timed out */ + uint32_t ips_forward; /* packets forwarded */ + uint32_t ips_cantforward; /* packets rcvd for unreachable dest */ + uint32_t ips_redirectsent; /* packets forwarded on same net */ + uint32_t ips_noproto; /* unknown or unsupported protocol */ + uint32_t ips_delivered; /* datagrams delivered to upper level*/ + uint32_t ips_localout; /* total ip packets generated here */ + uint32_t ips_odropped; /* lost packets due to nobufs, etc. */ + uint32_t ips_reassembled; /* total packets reassembled ok */ + uint32_t ips_fragmented; /* datagrams successfully fragmented */ + uint32_t ips_ofragments; /* output fragments created */ + uint32_t ips_cantfrag; /* don't fragment flag was set, etc. */ + uint32_t ips_badoptions; /* error in option processing */ + uint32_t ips_noroute; /* packets discarded due to no route */ + uint32_t ips_badvers; /* ip version != 4 */ + uint32_t ips_rawout; /* total raw ip packets generated */ + uint32_t ips_unaligned; /* times the ip packet was not aligned */ +}; + +extern struct ipstat ipstat; +extern struct ipq ipq; /* ip reass. queue */ +extern uint16_t ip_id; /* ip packet ctr, for ids */ +extern int ip_defttl; /* default IP ttl */ + + +extern void ip_init(void); +extern void ip_input(struct SLIRPmbuf *); +extern struct ip *ip_reass(register struct ipasfrag *, register struct ipq *); +extern void ip_freef(struct ipq *); +extern void ip_enq(register struct ipasfrag *, register struct ipasfrag *); +extern void ip_deq(register struct ipasfrag *); +extern void ip_slowtimo(void); +extern void ip_stripoptions(register struct SLIRPmbuf *, struct SLIRPmbuf *); + +extern int ip_output(struct SLIRPsocket *, struct SLIRPmbuf *); + +extern int cksum(struct SLIRPmbuf *m, int len); + + +#endif /*SLIRP_IP_H*/ diff --git a/src/ip_icmp.c b/src/ip_icmp.c new file mode 100644 index 0000000..4ee7781 --- /dev/null +++ b/src/ip_icmp.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 + * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp + */ +#ifdef _WIN32 +# include +# undef errno +# define errno (WSAGetLastError()) +#else +# include +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "ip.h" +#include "ip_icmp.h" +#include "icmp_var.h" +#include "udp.h" + + +struct icmpstat icmpstat; + + +/* The message sent when emulating PING */ +/* Be nice and tell them it's just a psuedo-ping packet */ +char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n"; + +/* list of actions for icmp_error() on RX of an icmp message */ +static int icmp_flush[19] = { +/* ECHO REPLY (0) */ 0, + 1, + 1, +/* DEST UNREACH (3) */ 1, +/* SOURCE QUENCH (4)*/ 1, +/* REDIRECT (5) */ 1, + 1, + 1, +/* ECHO (8) */ 0, +/* ROUTERADVERT (9) */ 1, +/* ROUTERSOLICIT (10) */ 1, +/* TIME EXCEEDED (11) */ 1, +/* PARAMETER PROBLEM (12) */ 1, +/* TIMESTAMP (13) */ 0, +/* TIMESTAMP REPLY (14) */ 0, +/* INFO (15) */ 0, +/* INFO REPLY (16) */ 0, +/* ADDR MASK (17) */ 0, +/* ADDR MASK REPLY (18) */ 0 +}; + + +/* Reflect the ip packet back to the source. */ +void +icmp_reflect(struct SLIRPmbuf *m) +{ + register struct ip *ip = mtod(m, struct ip *); + int hlen = ip->ip_hl << 2; + int optlen = hlen - sizeof(struct ip); + register struct icmp *icp; + struct in_addr icmp_dst; + + /* + * Send an icmp packet back to the ip level, + * after supplying a checksum. + */ + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + icp->icmp_cksum = 0; + icp->icmp_cksum = cksum(m, ip->ip_len - hlen); + + m->m_data -= hlen; + m->m_len += hlen; + + /* fill in ip */ + if (optlen > 0) { + /* + * Strip out original options by copying rest of first + * SLIRPmbuf's data back, and adjust the IP length. + */ + memmove((SLIRPcaddr_t)(ip + 1), (SLIRPcaddr_t)ip + hlen, + (unsigned )(m->m_len - hlen)); + hlen -= optlen; + ip->ip_hl = hlen >> 2; + ip->ip_len -= optlen; + m->m_len -= optlen; + } + + ip->ip_ttl = MAXTTL; + + /* swap */ + icmp_dst = ip->ip_dst; + ip->ip_dst = ip->ip_src; + ip->ip_src = icmp_dst; + + (void)ip_output((struct SLIRPsocket *)NULL, m); + + icmpstat.icps_reflect++; +} + + +/* Process a received ICMP message. */ +void +icmp_input(struct SLIRPmbuf *m, int hlen) +{ + register struct icmp *icp; + register struct ip *ip = mtod(m, struct ip *); + int icmplen = ip->ip_len; +#if 0 + int code; +#endif + + DEBUG_CALL("icmp_input"); + DEBUG_ARG("m = %lx", (long )m); + DEBUG_ARG("m_len = %d", m->m_len); + + icmpstat.icps_received++; + + /* + * Locate icmp structure in SLIRPmbuf, and check + * that its not corrupted and of at least minimum length. + */ + if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ + icmpstat.icps_tooshort++; +freeit: + m_freem(m); + goto end_error; + } + + m->m_len -= hlen; + m->m_data += hlen; + icp = mtod(m, struct icmp *); + if (cksum(m, icmplen)) { + icmpstat.icps_checksum++; + goto freeit; + } + m->m_len += hlen; + m->m_data -= hlen; + +#if 0 + icmpstat.icps_inhist[icp->icmp_type]++; + code = icp->icmp_code; +#endif + + DEBUG_ARG("icmp_type = %d", icp->icmp_type); + switch (icp->icmp_type) { + case ICMP_ECHO: + icp->icmp_type = ICMP_ECHOREPLY; + ip->ip_len += hlen; /* since ip_input subtracts this */ + if (ip->ip_dst.s_addr == router_addr.s_addr) { + icmp_reflect(m); + } else { + struct SLIRPsocket *so; + struct sockaddr_in addr; + + if ((so = socreate()) == NULL) goto freeit; + if (udp_attach(so) == -1) { + DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", + errno,strerror(errno))); + sofree(so); + m_free(m); + goto end_error; + } + so->so_m = m; + so->so_faddr = ip->ip_dst; + so->so_fport = htons(7); + so->so_laddr = ip->ip_src; + so->so_lport = htons(9); + so->so_iptos = ip->ip_tos; + so->so_type = IPPROTO_ICMP; + so->so_state = SS_ISFCONNECTED; + + /* Send the packet */ + addr.sin_family = AF_INET; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + /* It's an alias */ + switch (ntohl(so->so_faddr.s_addr) & 0xff) { + case CTL_SERVER: + addr.sin_addr = dns_addr; + break; + + case CTL_ROUTER: + default: + addr.sin_addr = loopback_addr; + break; + } + } else { + addr.sin_addr = so->so_faddr; + } + addr.sin_port = so->so_fport; + + if (sendto(so->s, icmp_ping_msg, + strlen(icmp_ping_msg), 0, + (struct sockaddr *)&addr, + sizeof(addr)) == -1) { + DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", + errno,strerror(errno))); + icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + udp_detach(so); + } + } + break; + + case ICMP_UNREACH: + /* XXX? report error? close socket? */ + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_SOURCEQUENCH: + case ICMP_TSTAMP: + case ICMP_MASKREQ: + case ICMP_REDIRECT: + icmpstat.icps_notsupp++; + m_freem(m); + break; + + default: + icmpstat.icps_badtype++; + m_freem(m); + } + +end_error: + /* m is m_free()'d xor put in a socket xor or given to ip_send */ + return; +} + + +/* + * Send an ICMP message in response to a situation + * + * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do). + * MUST NOT change this header information. + * MUST NOT reply to a multicast/broadcast IP address. + * MUST NOT reply to a multicast/broadcast MAC address. + * MUST reply to only the first fragment. + */ +/* + * Send ICMP_UNREACH back to the source regarding msrc. + * SLIRPmbuf *msrc is used as a template, but is NOT m_free()'d. + * It is reported as the bad ip packet. The header should + * be fully correct and in host byte order. + * ICMP fragmentation is illegal. All machines must accept 576 bytes in one + * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548 + */ +#define ICMP_MAXDATALEN (IP_MSS-28) + +void +icmp_error(struct SLIRPmbuf *msrc, u_char type, u_char code, int minsize, char *message) +{ + unsigned hlen, shlen, s_ip_len; + register struct ip *ip; + register struct icmp *icp; + register struct SLIRPmbuf *m; + u_int new_m_size; + + DEBUG_CALL("icmp_error"); + DEBUG_ARG("msrc = %lx", (long )msrc); + DEBUG_ARG("msrc_len = %d", msrc->m_len); + + if (type != ICMP_UNREACH && type != ICMP_TIMXCEED) + goto end_error; + + /* check msrc */ + if (! msrc) + goto end_error; + + /* Only reply to fragment 0. */ + ip = mtod(msrc, struct ip *); + if (ip->ip_off & IP_OFFMASK) + goto end_error; + + shlen = ip->ip_hl << 2; + s_ip_len = ip->ip_len; + if (ip->ip_p == IPPROTO_ICMP) { + icp = (struct icmp *)((char *)ip + shlen); + /* + * Assume any unknown ICMP type is an error. This isn't + * specified by the RFC, but think about it.. + */ + if (icp->icmp_type>18 || icmp_flush[icp->icmp_type]) + goto end_error; + } + + /* Copy msrc into m. */ + if (! (m=m_get())) + goto end_error; + new_m_size = sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN; + if (new_m_size > m->m_size) + m_inc(m, new_m_size); + memcpy(m->m_data, msrc->m_data, msrc->m_len); + m->m_len = msrc->m_len; + + /* make the header of the reply packet */ + ip = mtod(m, struct ip *); + hlen = sizeof(struct ip); /* no options in reply */ + + /* fill in icmp */ + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + if (minsize) + s_ip_len = shlen + ICMP_MINLEN; /* return header+8b only */ + else if (s_ip_len > ICMP_MAXDATALEN) /* maximum size */ + s_ip_len = ICMP_MAXDATALEN; + + m->m_len = ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */ + + /* min. size = 8+sizeof(struct ip)+8 */ + icp->icmp_type = type; + icp->icmp_code = code; + icp->icmp_id = 0; + icp->icmp_seq = 0; + + memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */ + HTONS(icp->icmp_ip.ip_len); + HTONS(icp->icmp_ip.ip_id); + HTONS(icp->icmp_ip.ip_off); + + icp->icmp_cksum = 0; + icp->icmp_cksum = cksum(m, m->m_len); + + m->m_data -= hlen; + m->m_len += hlen; + + /* fill in ip */ + ip->ip_hl = hlen >> 2; + ip->ip_len = (uint16_t)m->m_len; + ip->ip_tos = ((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */ + ip->ip_ttl = MAXTTL; + ip->ip_p = IPPROTO_ICMP; + ip->ip_dst = ip->ip_src; /* ip adresses */ + ip->ip_src = router_addr; + + (void)ip_output((struct SLIRPsocket *)NULL, m); + + icmpstat.icps_reflect++; + +end_error: + return; +} diff --git a/src/ip_icmp.h b/src/ip_icmp.h new file mode 100644 index 0000000..dbc4291 --- /dev/null +++ b/src/ip_icmp.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp + */ +#ifndef SLIRP_IP_ICMP_H +# define SLIRP_IP_ICMP_H + + +/* + * Interface Control Message Protocol Definitions. + * Per RFC 792, September 1981. + */ + +typedef uint32_t n_time; + +/* + * Structure of an icmp header. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct icmp { + uint8_t icmp_type; /* type of message, see below */ + uint8_t icmp_code; /* type sub code */ + uint16_t icmp_cksum; /* ones complement cksum of struct */ + union { + uint8_t ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + uint16_t icd_id; + uint16_t icd_seq; + } ih_idseq; + int ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + uint16_t ipm_void; + uint16_t ipm_nextmtu; + } ih_pmtu; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu + union { + struct id_ts { + n_time its_otime; + n_time its_rtime; + n_time its_ttime; + } id_ts; + struct id_ip { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + uint32_t id_mask; + char id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(0) +#endif + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first insure that the + * packet is large enought to contain the returned ip header. + * Only then can we do the check to see if 64 bits of packet + * data have been returned, since we need to check the returned + * ip header length. + */ +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */ +#define ICMP_MASKLEN 12 /* address mask */ +#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ +#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) + /* N.B.: must separately check that ip_hl >= 5 */ + +/* + * Definition of type and code field values. + */ +#define ICMP_ECHOREPLY 0 /* echo reply */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ +#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ +#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ +#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ +#define ICMP_REDIRECT 5 /* shorter route, codes: */ +#define ICMP_REDIRECT_NET 0 /* for network */ +#define ICMP_REDIRECT_HOST 1 /* for host */ +#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ +#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ +#define ICMP_ECHO 8 /* echo service */ +#define ICMP_ROUTERADVERT 9 /* router advertisement */ +#define ICMP_ROUTERSOLICIT 10 /* router solicitation */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ +#define ICMP_PARAMPROB 12 /* ip header bad */ +#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ +#define ICMP_TSTAMP 13 /* timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* timestamp reply */ +#define ICMP_IREQ 15 /* information request */ +#define ICMP_IREQREPLY 16 /* information reply */ +#define ICMP_MASKREQ 17 /* address mask request */ +#define ICMP_MASKREPLY 18 /* address mask reply */ + +#define ICMP_MAXTYPE 18 + +#define ICMP_INFOTYPE(type) \ + ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ + (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ + (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ + (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ + (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) + + +extern void icmp_reflect(struct SLIRPmbuf *m); +extern void icmp_input(struct SLIRPmbuf *, int); +extern void icmp_error(struct SLIRPmbuf *, uint8_t, uint8_t, int, char *); + + +#endif /*SLIRP_IP_ICMP_H*/ diff --git a/src/ip_input.c b/src/ip_input.c new file mode 100644 index 0000000..1a120b0 --- /dev/null +++ b/src/ip_input.c @@ -0,0 +1,704 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 + * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "ip.h" +#include "ip_icmp.h" +#include "tcp.h" +#include "udp.h" + + +/* + * Changes and additions relating to SLiRP are + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +int ip_defttl; +struct ipstat ipstat; +struct ipq ipq; + +/* + * IP initialization: fill in IP protocol switch table. + * All protocols not implemented in kernel go to raw IP protocol handler. + */ +void +ip_init() +{ + ipq.next = ipq.prev = (ipqp_32)&ipq; + ip_id = 0x1234; /* was: tt.tv_sec & 0xffff */ + + udp_init(); + tcp_init(); + + ip_defttl = IPDEFTTL; +} + +/* + * Ip input routine. Checksum and byte swap header. If fragmented + * try to reassemble. Process options. Pass to next level. + */ +void +ip_input(struct SLIRPmbuf *m) +{ + register struct ip *ip; + u_int hlen; + + DEBUG_CALL("ip_input"); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m_len = %d", m->m_len); + + ipstat.ips_total++; + + if (m->m_len < sizeof (struct ip)) { + ipstat.ips_toosmall++; + return; + } + + ip = mtod(m, struct ip *); + + if (ip->ip_v != IPVERSION) { + ipstat.ips_badvers++; + goto bad; + } + + hlen = ip->ip_hl << 2; + if (hlen<(u_int)sizeof(struct ip ) || hlen>(u_int)m->m_len) {/* min header length */ + ipstat.ips_badhlen++; /* or packet too short */ + goto bad; + } + + /* keep ip header intact for ICMP reply + * ip->ip_sum = cksum(m, hlen); + * if (ip->ip_sum) { + */ + if(cksum(m,hlen)) { + ipstat.ips_badsum++; + goto bad; + } + + /* + * Convert fields to host representation. + */ + NTOHS(ip->ip_len); + if (ip->ip_len < hlen) { + ipstat.ips_badlen++; + goto bad; + } + NTOHS(ip->ip_id); + NTOHS(ip->ip_off); + + /* + * Check that the amount of data in the buffers + * is as at least much as the IP header would have us expect. + * Trim SLIRPmbufs if longer than we expect. + * Drop packet if shorter than we expect. + */ + if (m->m_len < ip->ip_len) { + ipstat.ips_tooshort++; + goto bad; + } + /* Should drop packet if SLIRPmbuf too long? hmmm... */ + if (m->m_len > ip->ip_len) + m_adj(m, ip->ip_len - m->m_len); + + /* check ip_ttl for a correct ICMP reply */ + if(ip->ip_ttl==0 || ip->ip_ttl==1) { + icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); + goto bad; + } + + /* + * Process options and, if not destined for us, + * ship it on. ip_dooptions returns 1 when an + * error was detected (causing an icmp message + * to be sent and the original packet to be freed). + */ +/* We do no IP options */ +/* if (hlen > sizeof (struct ip) && ip_dooptions(m)) + * goto next; + */ + /* + * If offset or IP_MF are set, must reassemble. + * Otherwise, nothing need be done. + * (We could look in the reassembly queue to see + * if the packet was previously fragmented, + * but it's not worth the time; just let them time out.) + * + * XXX This should fail, don't fragment yet + */ + if (ip->ip_off &~ IP_DF) { + register struct ipq *fp; + /* + * Look for queue of fragments + * of this datagram. + */ + for (fp = (struct ipq *) ipq.next; fp != &ipq; + fp = (struct ipq *) fp->next) + if (ip->ip_id == fp->ipq_id && + ip->ip_src.s_addr == fp->ipq_src.s_addr && + ip->ip_dst.s_addr == fp->ipq_dst.s_addr && + ip->ip_p == fp->ipq_p) + goto found; + fp = 0; + found: + + /* + * Adjust ip_len to not reflect header, + * set ip_mff if more fragments are expected, + * convert offset of this to bytes. + */ + ip->ip_len -= hlen; + if (ip->ip_off & IP_MF) + ((struct ipasfrag *)ip)->ipf_mff |= 1; + else + ((struct ipasfrag *)ip)->ipf_mff &= ~1; + + ip->ip_off <<= 3; + + /* + * If datagram marked as having more fragments + * or if this is not the first fragment, + * attempt reassembly; if it succeeds, proceed. + */ + if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) { + ipstat.ips_fragments++; + ip = ip_reass((struct ipasfrag *)ip, fp); + if (ip == 0) + return; + ipstat.ips_reassembled++; + m = dtom(ip); + } else + if (fp) + ip_freef(fp); + + } else + ip->ip_len -= hlen; + + /* + * Switch out to protocol's input routine. + */ + ipstat.ips_delivered++; + switch (ip->ip_p) { + case IPPROTO_TCP: + tcp_input(m, hlen, (struct SLIRPsocket *)NULL); + break; + case IPPROTO_UDP: + udp_input(m, hlen); + break; + case IPPROTO_ICMP: + icmp_input(m, hlen); + break; + default: + ipstat.ips_noproto++; + m_free(m); + } + return; +bad: + m_freem(m); + return; +} + +/* + * Take incoming datagram fragment and try to + * reassemble it into whole datagram. If a chain for + * reassembly of this datagram already exists, then it + * is given as fp; otherwise have to make a chain. + */ +struct ip * +ip_reass(register struct ipasfrag *ip, register struct ipq *fp) +{ + register struct SLIRPmbuf *m = dtom(ip); + register struct ipasfrag *q; + int hlen = ip->ip_hl << 2; + int i, next; + + DEBUG_CALL("ip_reass"); + DEBUG_ARG("ip = %lx", (long)ip); + DEBUG_ARG("fp = %lx", (long)fp); + DEBUG_ARG("m = %lx", (long)m); + + /* + * Presence of header sizes in SLIRPmbufs + * would confuse code below. + * Fragment m_data is concatenated. + */ + m->m_data += hlen; + m->m_len -= hlen; + + /* + * If first fragment to arrive, create a reassembly queue. + */ + if (fp == 0) { + struct SLIRPmbuf *t; + if ((t = m_get()) == NULL) goto dropfrag; + fp = mtod(t, struct ipq *); + insque_32(fp, &ipq); + fp->ipq_ttl = IPFRAGTTL; + fp->ipq_p = ip->ip_p; + fp->ipq_id = ip->ip_id; + fp->ipq_next = fp->ipq_prev = (ipasfragp_32)fp; + fp->ipq_src = ((struct ip *)ip)->ip_src; + fp->ipq_dst = ((struct ip *)ip)->ip_dst; + q = (struct ipasfrag *)fp; + goto insert; + } + + /* + * Find a segment which begins after this one does. + */ + for (q = (struct ipasfrag *)fp->ipq_next; q != (struct ipasfrag *)fp; + q = (struct ipasfrag *)q->ipf_next) + if (q->ip_off > ip->ip_off) + break; + + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if (q->ipf_prev != (ipasfragp_32)fp) { + i = ((struct ipasfrag *)(q->ipf_prev))->ip_off + + ((struct ipasfrag *)(q->ipf_prev))->ip_len - ip->ip_off; + if (i > 0) { + if (i >= ip->ip_len) + goto dropfrag; + m_adj(dtom(ip), i); + ip->ip_off += i; + ip->ip_len -= i; + } + } + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) { + i = (ip->ip_off + ip->ip_len) - q->ip_off; + if (i < q->ip_len) { + q->ip_len -= i; + q->ip_off += i; + m_adj(dtom(q), i); + break; + } + q = (struct ipasfrag *) q->ipf_next; + m_freem(dtom((struct ipasfrag *) q->ipf_prev)); + ip_deq((struct ipasfrag *) q->ipf_prev); + } + +insert: + /* + * Stick new segment in its place; + * check for complete reassembly. + */ + ip_enq(ip, (struct ipasfrag *) q->ipf_prev); + next = 0; + for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp; + q = (struct ipasfrag *) q->ipf_next) { + if (q->ip_off != next) + return (0); + next += q->ip_len; + } + if (((struct ipasfrag *)(q->ipf_prev))->ipf_mff & 1) + return (0); + + /* + * Reassembly is complete; concatenate fragments. + */ + q = (struct ipasfrag *) fp->ipq_next; + m = dtom(q); + + q = (struct ipasfrag *) q->ipf_next; + while (q != (struct ipasfrag *)fp) { + struct SLIRPmbuf *t; + t = dtom(q); + q = (struct ipasfrag *) q->ipf_next; + m_cat(m, t); + } + + /* + * Create header for new ip packet by + * modifying header of first packet; + * dequeue and discard fragment reassembly header. + * Make header visible. + */ + ip = (struct ipasfrag *) fp->ipq_next; + + /* + * If the fragments concatenated to an SLIRPmbuf that's + * bigger than the total size of the fragment, then and + * m_ext buffer was alloced. But fp->ipq_next points to + * the old buffer (in the SLIRPmbuf), so we must point ip + * into the new buffer. + */ + if (m->m_flags & M_EXT) { + int delta; + delta = (char *)ip - m->m_dat; + ip = (struct ipasfrag *)(m->m_ext + delta); + } + + /* DEBUG_ARG("ip = %lx", (long)ip); + * ip=(struct ipasfrag *)m->m_data; */ + + ip->ip_len = next; + ip->ipf_mff &= ~1; + ((struct ip *)ip)->ip_src = fp->ipq_src; + ((struct ip *)ip)->ip_dst = fp->ipq_dst; + remque_32(fp); + (void) m_free(dtom(fp)); + m = dtom(ip); + m->m_len += (ip->ip_hl << 2); + m->m_data -= (ip->ip_hl << 2); + + return ((struct ip *)ip); + +dropfrag: + ipstat.ips_fragdropped++; + m_freem(m); + return (0); +} + +/* + * Free a fragment reassembly header and all + * associated datagrams. + */ +void +ip_freef(struct ipq *fp) +{ + register struct ipasfrag *q, *p; + + for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp; + q = p) { + p = (struct ipasfrag *) q->ipf_next; + ip_deq(q); + m_freem(dtom(q)); + } + remque_32(fp); + (void) m_free(dtom(fp)); +} + +/* + * Put an ip fragment on a reassembly chain. + * Like insque, but pointers in middle of structure. + */ +void +ip_enq(struct ipasfrag *p, struct ipasfrag *prev) +{ + DEBUG_CALL("ip_enq"); + DEBUG_ARG("prev = %lx", (long)prev); + p->ipf_prev = (ipasfragp_32) prev; + p->ipf_next = prev->ipf_next; + ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = (ipasfragp_32) p; + prev->ipf_next = (ipasfragp_32) p; +} + +/* + * To ip_enq as remque is to insque. + */ +void +ip_deq(register struct ipasfrag *p) +{ + ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next; + ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev; +} + +/* + * IP timer processing; + * if a timer expires on a reassembly + * queue, discard it. + */ +void +ip_slowtimo(void) +{ + register struct ipq *fp; + + DEBUG_CALL("ip_slowtimo"); + + fp = (struct ipq *) ipq.next; + if (fp == 0) + return; + + while (fp != &ipq) { + --fp->ipq_ttl; + fp = (struct ipq *) fp->next; + if (((struct ipq *)(fp->prev))->ipq_ttl == 0) { + ipstat.ips_fragtimeout++; + ip_freef((struct ipq *) fp->prev); + } + } +} + +/* + * Do option processing on a datagram, + * possibly discarding it if bad options are encountered, + * or forwarding it if source-routed. + * Returns 1 if packet has been forwarded/freed, + * 0 if the packet should be processed further. + */ + +#ifdef notdef + +int +ip_dooptions(struct SLIRPmbuf *m) +{ + register struct ip *ip = mtod(m, struct ip *); + register u_char *cp; + register struct ip_timestamp *ipt; + register struct in_ifaddr *ia; +/* int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; */ + int opt, optlen, cnt, off, code, type, forward = 0; + struct in_addr *sin, dst; +typedef u_int32_t n_time; + n_time ntime; + + dst = ip->ip_dst; + cp = (u_char *)(ip + 1); + cnt = (ip->ip_hl << 2) - sizeof (struct ip); + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[IPOPT_OPTVAL]; + if (opt == IPOPT_EOL) + break; + if (opt == IPOPT_NOP) + optlen = 1; + else { + optlen = cp[IPOPT_OLEN]; + if (optlen <= 0 || optlen > cnt) { + code = &cp[IPOPT_OLEN] - (u_char *)ip; + goto bad; + } + } + switch (opt) { + + default: + break; + + /* + * Source routing with record. + * Find interface with current destination address. + * If none on this machine then drop if strictly routed, + * or do nothing if loosely routed. + * Record interface address and bring up next address + * component. If strictly routed make sure next + * address is on directly accessible net. + */ + case IPOPT_LSRR: + case IPOPT_SSRR: + if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { + code = &cp[IPOPT_OFFSET] - (u_char *)ip; + goto bad; + } + ipaddr.sin_addr = ip->ip_dst; + ia = (struct in_ifaddr *) + ifa_ifwithaddr((struct sockaddr *)&ipaddr); + if (ia == 0) { + if (opt == IPOPT_SSRR) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_SRCFAIL; + goto bad; + } + /* + * Loose routing, and not at next destination + * yet; nothing to do except forward. + */ + break; + } + off--; / * 0 origin * / + if (off > optlen - sizeof(struct in_addr)) { + /* + * End of source route. Should be for us. + */ + save_rte(cp, ip->ip_src); + break; + } + /* + * locate outgoing interface + */ + bcopy((SLIRPcaddr_t)(cp + off), (SLIRPcaddr_t)&ipaddr.sin_addr, + sizeof(ipaddr.sin_addr)); + if (opt == IPOPT_SSRR) { +#define INA struct in_ifaddr * +#define SA struct sockaddr * + if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0) + ia = (INA)ifa_ifwithnet((SA)&ipaddr); + } else + ia = ip_rtaddr(ipaddr.sin_addr); + if (ia == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_SRCFAIL; + goto bad; + } + ip->ip_dst = ipaddr.sin_addr; + bcopy((SLIRPcaddr_t)&(IA_SIN(ia)->sin_addr), + (SLIRPcaddr_t)(cp + off), sizeof(struct in_addr)); + cp[IPOPT_OFFSET] += sizeof(struct in_addr); + /* + * Let ip_intr's mcast routing check handle mcast pkts + */ + forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr)); + break; + + case IPOPT_RR: + if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { + code = &cp[IPOPT_OFFSET] - (u_char *)ip; + goto bad; + } + /* + * If no space remains, ignore. + */ + off--; * 0 origin * + if (off > optlen - sizeof(struct in_addr)) + break; + bcopy((SLIRPcaddr_t)(&ip->ip_dst), (SLIRPcaddr_t)&ipaddr.sin_addr, + sizeof(ipaddr.sin_addr)); + /* + * locate outgoing interface; if we're the destination, + * use the incoming interface (should be same). + */ + if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 && + (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_HOST; + goto bad; + } + bcopy((SLIRPcaddr_t)&(IA_SIN(ia)->sin_addr), + (SLIRPcaddr_t)(cp + off), sizeof(struct in_addr)); + cp[IPOPT_OFFSET] += sizeof(struct in_addr); + break; + + case IPOPT_TS: + code = cp - (u_char *)ip; + ipt = (struct ip_timestamp *)cp; + if (ipt->ipt_len < 5) + goto bad; + if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) { + if (++ipt->ipt_oflw == 0) + goto bad; + break; + } + sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); + switch (ipt->ipt_flg) { + + case IPOPT_TS_TSONLY: + break; + + case IPOPT_TS_TSANDADDR: + if (ipt->ipt_ptr + sizeof(n_time) + + sizeof(struct in_addr) > ipt->ipt_len) + goto bad; + ipaddr.sin_addr = dst; + ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr, + m->m_pkthdr.rcvif); + if (ia == 0) + continue; + bcopy((SLIRPcaddr_t)&IA_SIN(ia)->sin_addr, + (SLIRPcaddr_t)sin, sizeof(struct in_addr)); + ipt->ipt_ptr += sizeof(struct in_addr); + break; + + case IPOPT_TS_PRESPEC: + if (ipt->ipt_ptr + sizeof(n_time) + + sizeof(struct in_addr) > ipt->ipt_len) + goto bad; + bcopy((SLIRPcaddr_t)sin, (SLIRPcaddr_t)&ipaddr.sin_addr, + sizeof(struct in_addr)); + if (ifa_ifwithaddr((SA)&ipaddr) == 0) + continue; + ipt->ipt_ptr += sizeof(struct in_addr); + break; + + default: + goto bad; + } + ntime = iptime(); + bcopy((SLIRPcaddr_t)&ntime, (SLIRPcaddr_t)cp + ipt->ipt_ptr - 1, + sizeof(n_time)); + ipt->ipt_ptr += sizeof(n_time); + } + } + if (forward) { + ip_forward(m, 1); + return (1); + } + } + } + return (0); +bad: + /* ip->ip_len -= ip->ip_hl << 2; XXX icmp_error adds in hdr length */ + +/* Not yet */ + icmp_error(m, type, code, 0, 0); + + ipstat.ips_badoptions++; + return (1); +} + +#endif /* notdef */ + +/* + * Strip out IP options, at higher + * level protocol in the kernel. + * Second argument is buffer to which options + * will be moved, and return value is their length. + * (XXX) should be deleted; last arg currently ignored. + */ +void +ip_stripoptions(struct SLIRPmbuf *m, struct SLIRPmbuf *mopt) +{ + register int i; + struct ip *ip = mtod(m, struct ip *); + register SLIRPcaddr_t opts; + int olen; + + olen = (ip->ip_hl<<2) - sizeof (struct ip); + opts = (SLIRPcaddr_t)(ip + 1); + i = m->m_len - (sizeof (struct ip) + olen); + memcpy(opts, opts + olen, (unsigned)i); + m->m_len -= olen; + + ip->ip_hl = sizeof(struct ip) >> 2; +} diff --git a/src/ip_output.c b/src/ip_output.c new file mode 100644 index 0000000..56e5b11 --- /dev/null +++ b/src/ip_output.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 + * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" + + +/* + * Changes and additions relating to SLiRP are + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + + +uint16_t ip_id; + + +/* + * IP output. The packet in SLIRPmbuf chain m contains a skeletal IP + * header (with len, off, ttl, proto, tos, src, dst). + * The SLIRPmbuf chain containing the packet will be freed. + * The SLIRPmbuf opt, if present, will not be freed. + */ +int +ip_output(struct SLIRPsocket *so, struct SLIRPmbuf *m0) +{ + u_int hlen = sizeof(struct ip); + struct SLIRPmbuf **mnext; + struct SLIRPmbuf *m = m0; + struct ip *ip, *mhip; + int mhlen, firstlen; + u_int len, off; + int error = 0; + + DEBUG_CALL("ip_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m0 = %lx", (long)m0); + + /* We do no options */ +#if 0 + if (opt) { + m = ip_insertoptions(m, opt, &len); + hlen = len; + } +#endif + + /* Fill in IP header. */ + ip = mtod(m, struct ip *); + ip->ip_v = IPVERSION; + ip->ip_off &= IP_DF; + ip->ip_id = htons(ip_id++); + ip->ip_hl = hlen >> 2; + ipstat.ips_localout++; + + /* + * Verify that we have any chance at all of being able to queue + * the packet or packet fragments + */ +#if 0 + /* XXX Hmmm... */ + if (if_queued > if_thresh && towrite <= 0) { + error = ENOBUFS; + goto bad; + } +#endif + + /* If small enough for interface, can just send directly. */ + if ((uint16_t)ip->ip_len <= if_mtu) { + ip->ip_len = htons((uint16_t)ip->ip_len); + ip->ip_off = htons((uint16_t)ip->ip_off); + ip->ip_sum = 0; + ip->ip_sum = cksum(m, hlen); + + if_output(so, m); + goto done; + } + + /* + * Too large for interface; fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ + if (ip->ip_off & IP_DF) { + error = -1; + ipstat.ips_cantfrag++; + goto bad; + } + + len = (if_mtu - hlen) &~ 7; /* ip databytes per packet */ + if (len < 8) { + error = -1; + goto bad; + } + + mnext = &m->m_nextpkt; + firstlen = len; + + /* + * Loop through length of segment after first fragment, + * make new header and copy data of each part and link onto chain. + */ + m0 = m; + mhlen = sizeof (struct ip); + for (off = hlen + len; off < ip->ip_len; off += len) { + m = m_get(); + if (m == 0) { + error = -1; + ipstat.ips_odropped++; + goto sendorfree; + } + m->m_data += if_maxlinkhdr; + mhip = mtod(m, struct ip *); + *mhip = *ip; + + /* No options */ +#if 0 + if (hlen > sizeof (struct ip)) { + mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + mhip->ip_hl = mhlen >> 2; + } +#endif + m->m_len = mhlen; + mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); + if (ip->ip_off & IP_MF) + mhip->ip_off |= IP_MF; + if (off + len >= (uint16_t)ip->ip_len) + len = (uint16_t)ip->ip_len - off; + else + mhip->ip_off |= IP_MF; + mhip->ip_len = htons((uint16_t)(len + mhlen)); + + if (m_copy(m, m0, off, len) < 0) { + error = -1; + goto sendorfree; + } + + mhip->ip_off = htons((uint16_t)mhip->ip_off); + mhip->ip_sum = 0; + mhip->ip_sum = cksum(m, mhlen); + *mnext = m; + mnext = &m->m_nextpkt; + ipstat.ips_ofragments++; + } + + /* + * Update first fragment by trimming what's been copied out + * and updating header, then send each fragment (in order). + */ + m = m0; + m_adj(m, hlen + firstlen - ip->ip_len); + ip->ip_len = htons((uint16_t)m->m_len); + ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF)); + ip->ip_sum = 0; + ip->ip_sum = cksum(m, hlen); +sendorfree: + for (m = m0; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + if (error == 0) + if_output(so, m); + else + m_freem(m); + } + + if (error == 0) + ipstat.ips_fragmented++; + +done: + return(error); + +bad: + m_freem(m0); + goto done; +} diff --git a/src/mbuf.c b/src/mbuf.c new file mode 100644 index 0000000..0f5288b --- /dev/null +++ b/src/mbuf.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1995 Danny Gasparovski + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" // merge with slirp.h +#include "mbuf.h" +#include "if.h" + + +/* + * mbuf's in SLiRP are much simpler than the real mbufs in + * FreeBSD. They are fixed size, determined by the MTU, + * so that one whole packet can fit. Mbuf's cannot be + * chained together. If there's more data than the mbuf + * could hold, an external malloced buffer is pointed to + * by m_ext (and the data pointers) and M_EXT is set in + * the flags + */ +struct mbuf *mbutl; +char *mclrefcnt; +int mbuf_alloced = 0; +struct SLIRPmbuf m_freelist, m_usedlist; +int mbuf_thresh = 30; +int mbuf_max = 0; +size_t msize; + +void +m_init(void) +{ + m_freelist.m_next = m_freelist.m_prev = &m_freelist; + m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist; + msize_init(); +} + +void msize_init(void) +{ + /* + * Find a nice value for msize + * XXX if_maxlinkhdr already in mtu + */ + msize = (if_mtu>if_mru?if_mtu:if_mru) + + if_maxlinkhdr + sizeof(struct m_hdr ) + 6; +} + +/* + * Get an mbuf from the free list, if there are none + * malloc one + * + * Because fragmentation can occur if we alloc new mbufs and + * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE, + * which tells m_free to actually free() it + */ +struct SLIRPmbuf * m_get(void) +{ + struct SLIRPmbuf *m; + int flags = 0; + + DEBUG_CALL("m_get"); + + if (m_freelist.m_next == &m_freelist) { + m = (struct SLIRPmbuf *)malloc(msize); + if (m == NULL) goto end_error; + mbuf_alloced++; + if (mbuf_alloced > mbuf_thresh) + flags = M_DOFREE; + if (mbuf_alloced > mbuf_max) + mbuf_max = mbuf_alloced; + } else { + m = m_freelist.m_next; + remque(m); + } + + /* Insert it in the used list */ + insque(m,&m_usedlist); + m->m_flags = (flags | M_USEDLIST); + + /* Initialise it */ + m->m_size = msize - sizeof(struct m_hdr); + m->m_data = m->m_dat; + m->m_len = 0; + m->m_nextpkt = 0; + m->m_prevpkt = 0; +end_error: + DEBUG_ARG("m = %lx", (long )m); + return m; +} + +//For some reason this fails in GDB saying tehre is no m_flags member +void +m_free(struct SLIRPmbuf *m) +{ + + DEBUG_CALL("m_free"); + DEBUG_ARG("m = %lx", (long )m); + + if(m) { + /* Remove from m_usedlist */ + if (m->m_flags & M_USEDLIST) + remque(m); + + + + /* If it's M_EXT, free() it */ + if (m->m_flags & M_EXT) + free(m->m_ext); + + /* + * Either free() it or put it on the free list + */ + if (m->m_flags & M_DOFREE) { + free(m); + mbuf_alloced--; + } else if ((m->m_flags & M_FREELIST) == 0) { + insque(m,&m_freelist); + m->m_flags = M_FREELIST; /* Clobber other flags */ + } + } /* if(m) */ +} + +/* + * Copy data from one mbuf to the end of + * the other.. if result is too big for one mbuf, malloc() + * an M_EXT data segment + */ +void +m_cat(struct SLIRPmbuf *m, struct SLIRPmbuf *n) +{ + /* + * If there's no room, realloc + */ + if (M_FREEROOM(m) < n->m_len) + m_inc(m,m->m_size+MINCSIZE); + + memcpy(m->m_data+m->m_len, n->m_data, n->m_len); + m->m_len += n->m_len; + + m_free(n); +} + + +/* make m size bytes large */ +void +m_inc(struct SLIRPmbuf *m, int size) +{ + int datasize; + + /* some compiles throw up on gotos. This one we can fake. */ + if (m->m_size > (u_int)size) return; + + if (m->m_flags & M_EXT) { + datasize = m->m_data - m->m_ext; + m->m_ext = (char *)realloc(m->m_ext,size); +/* if (m->m_ext == NULL) + * return (struct SLIRPmbuf *)NULL; + */ + m->m_data = m->m_ext + datasize; + } else { + char *dat; + datasize = m->m_data - m->m_dat; + dat = (char *)malloc(size); +/* if (dat == NULL) + * return (struct SLIRPmbuf *)NULL; + */ + memcpy(dat, m->m_dat, m->m_size); + + m->m_ext = dat; + m->m_data = m->m_ext + datasize; + m->m_flags |= M_EXT; + } + + m->m_size = size; + +} + + + +void +m_adj(struct SLIRPmbuf *m, int len) +{ + if (m == NULL) + return; + if (len >= 0) { + /* Trim from head */ + m->m_data += len; + m->m_len -= len; + } else { + /* Trim from tail */ + len = -len; + m->m_len -= len; + } +} + + +/* + * Copy len bytes from m, starting off bytes into n + */ +int +m_copy(struct SLIRPmbuf *n, struct SLIRPmbuf *m, int off, int len) +{ + if (len > M_FREEROOM(n)) + return -1; + + memcpy((n->m_data + n->m_len), (m->m_data + off), len); + n->m_len += len; + return 0; +} + + +/* + * Given a pointer into an mbuf, return the mbuf + * XXX This is a kludge, I should eliminate the need for it + * Fortunately, it's not used often + */ +struct SLIRPmbuf * +dtom(void *dat) +{ + struct SLIRPmbuf *m; + + DEBUG_CALL("dtom"); + DEBUG_ARG("dat = %lx", (long )dat); + + /* bug corrected for M_EXT buffers */ + for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) { + if (m->m_flags & M_EXT) { + if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) ) + return m; + } else { + if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) ) + return m; + } + } + + DEBUG_ERROR((dfd, "dtom failed")); + + return (struct SLIRPmbuf *)0; +} + diff --git a/src/mbuf.h b/src/mbuf.h new file mode 100644 index 0000000..88912aa --- /dev/null +++ b/src/mbuf.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mbuf.h 8.3 (Berkeley) 1/21/94 + * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp + */ +#ifndef SLIRP_MBUF_H +# define SLIRP_MBUF_H + + +#define m_freem m_free + + +#define MINCSIZE 4096 /* Amount to increase mbuf if too small */ + +/* + * Macros for type conversion + * mtod(m,t) - convert mbuf pointer to data pointer of correct type + * dtom(x) - convert data pointer within mbuf to mbuf pointer (XXX) + */ +#define mtod(m,t) ((t)(m)->m_data) +/* #define dtom(x) ((struct SLIRPmbuf *)((int)(x) & ~(M_SIZE-1))) */ + +/* XXX About mbufs for slirp: + * Only one mbuf is ever used in a chain, for each "cell" of data. + * m_nextpkt points to the next packet, if fragmented. + * If the data is too large, the M_EXT is used, and a larger block + * is alloced. Therefore, m_free[m] must check for M_EXT and if set + * free the m_ext. This is inefficient memory-wise, but who cares. + */ + +/* XXX should union some of these! */ +/* header at beginning of each mbuf: */ +struct m_hdr { + struct SLIRPmbuf *mh_next; /* Linked list of mbufs */ + struct SLIRPmbuf *mh_prev; + struct SLIRPmbuf *mh_nextpkt; /* Next packet in queue/record */ + struct SLIRPmbuf *mh_prevpkt; /* Flags aren't used in the output queue */ + int mh_flags; /* Misc flags */ + + size_t mh_size; /* Size of data */ + struct SLIRPsocket *mh_so; + + SLIRPcaddr_t mh_data; /* Location of data */ + int32_t mh_len; /* Amount of data in this mbuf */ +}; + +/* + * How much room is in the mbuf, from m_data to the end of the mbuf + */ +#define M_ROOM(m) ((m->m_flags & M_EXT)? \ + (((m)->m_ext + (m)->m_size) - (m)->m_data) \ + : \ + (((m)->m_dat + (m)->m_size) - (m)->m_data)) + +/* + * How much free room there is + */ +#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len) +#define M_TRAILINGSPACE M_FREEROOM + +struct SLIRPmbuf { + struct m_hdr m_hdr; + union M_dat { + char m_dat_[1]; /* ANSI don't like 0 sized arrays */ + char *m_ext_; + } M_dat; +}; + +#define m_next m_hdr.mh_next +#define m_prev m_hdr.mh_prev +#define m_nextpkt m_hdr.mh_nextpkt +#define m_prevpkt m_hdr.mh_prevpkt +#define m_flags m_hdr.mh_flags +#define m_len m_hdr.mh_len +#define m_data m_hdr.mh_data +#define m_size m_hdr.mh_size +#define m_dat M_dat.m_dat_ +#define m_ext M_dat.m_ext_ +#define m_so m_hdr.mh_so + +#define ifq_prev m_prev +#define ifq_next m_next +#define ifs_prev m_prevpkt +#define ifs_next m_nextpkt +#define ifq_so m_so + +#define M_EXT 0x01 /* m_ext points to more (malloced) data */ +#define M_FREELIST 0x02 /* mbuf is on free list */ +#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */ +#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free() + * it rather than putting it on the free list */ + +/* + * Mbuf statistics. XXX + */ + +struct mbstat { + int mbs_alloced; /* Number of mbufs allocated */ + +}; + + +extern struct mbstat mbstat; +extern int mbuf_alloced; +extern struct SLIRPmbuf m_freelist, m_usedlist; +extern int mbuf_max; + + +extern void m_init(void); +extern void msize_init(void); +extern struct SLIRPmbuf *m_get(void); +extern void m_free(struct SLIRPmbuf *); +extern void m_cat(register struct SLIRPmbuf *, register struct SLIRPmbuf *); +extern void m_inc(struct SLIRPmbuf *, int); +extern void m_adj(struct SLIRPmbuf *, int); +extern int m_copy(struct SLIRPmbuf *, struct SLIRPmbuf *, int, int); +extern struct SLIRPmbuf *dtom(void *); + + +#endif /*SLIRP_MBUF_H*/ diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..50a6e71 --- /dev/null +++ b/src/misc.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#include +#include +#include +#include +#include +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +# include +#endif +#include "slirp.h" +#include "debug.h" // merge with slirp.h + + +//#if SIZEOF_CHAR_P == 8 +struct quehead_32 { + uintptr_t qh_link; + uintptr_t qh_rlink; +}; + + +inline void +insque_32(void *a, void *b) +{ + register struct quehead_32 *el = (struct quehead_32 *) a; + register struct quehead_32 *head = (struct quehead_32 *) b; + + el->qh_link = head->qh_link; + head->qh_link = (uintptr_t)el; + el->qh_rlink = (uintptr_t)head; + ((struct quehead_32 *)(el->qh_link))->qh_rlink = (uintptr_t)el; +} + + +inline void +remque_32(void *a) +{ + register struct quehead_32 *el = (struct quehead_32 *) a; + + ((struct quehead_32 *)(el->qh_link))->qh_rlink = el->qh_rlink; + ((struct quehead_32 *)(el->qh_rlink))->qh_link = el->qh_link; + el->qh_rlink = 0; +} +//#endif /* SIZEOF_CHAR_P == 8 */ + + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + + +void +insque(void *a, void *b) +{ + register struct quehead *el = (struct quehead *) a; + register struct quehead *head = (struct quehead *) b; + + el->qh_link = head->qh_link; + head->qh_link = (struct quehead *)el; + el->qh_rlink = (struct quehead *)head; + ((struct quehead *)(el->qh_link))->qh_rlink = (struct quehead *)el; +} + + +void +remque(void *a) +{ + register struct quehead *el = (struct quehead *) a; + + ((struct quehead *)(el->qh_link))->qh_rlink = el->qh_rlink; + ((struct quehead *)(el->qh_rlink))->qh_link = el->qh_link; + el->qh_rlink = NULL; +#if 0 + el->qh_link = NULL; /* TCP FIN1 crashes if you do this. Why ? */ +#endif +} + + +#ifndef HAVE_STRERROR +/* For systems with no strerror. */ +char * +SLIRPstrerror(int error) +{ + if (error < sys_nerr) + return sys_errlist[error]; + + return "Unknown error."; +} +#endif + + +/* Set fd blocking and non-blocking. */ +void +fd_nonblock(int fd) +{ +#if defined(USE_FIONBIO) && defined(FIONBIO) + ioctlsockopt_t opt = 1; + + ioctlsocket(fd, FIONBIO, &opt); +#else + int opt; + + opt = fcntl(fd, F_GETFL, 0); + opt |= O_NONBLOCK; + fcntl(fd, F_SETFL, opt); +#endif +} + + +void +fd_block(int fd) +{ +#if defined(USE_FIONBIO) && defined(FIONBIO) + ioctlsockopt_t opt = 0; + + ioctlsocket(fd, FIONBIO, &opt); +#else + int opt; + + opt = fcntl(fd, F_GETFL, 0); + opt &= ~O_NONBLOCK; + fcntl(fd, F_SETFL, opt); +#endif +} diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..9461812 --- /dev/null +++ b/src/misc.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifndef SLIRP_MISC_H +# define SLIRP_MISC_H + + +#ifdef USE_REDIR +struct ex_list { + int ex_pty; /* Do we want a pty? */ + int ex_addr; /* The last byte of the address */ + int ex_fport; /* Port to telnet to */ + char *ex_exec; /* Command line of what to exec */ + + struct ex_list *ex_next; +}; + +struct emu_t { + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; + + struct emu_t *next; +}; + + +extern struct emu_t *tcpemu; +extern int x_port, x_server, x_display; +extern struct ex_list *exec_list; +#endif + + +#define EMU_NONE 0x0 + +/* TCP emulations */ +#define EMU_CTL 0x1 +#define EMU_FTP 0x2 +#define EMU_KSH 0x3 +#define EMU_IRC 0x4 +#define EMU_REALAUDIO 0x5 +#define EMU_RLOGIN 0x6 +#define EMU_IDENT 0x7 +#define EMU_RSH 0x8 + +#define EMU_NOCONNECT 0x10 /* Don't connect */ + +/* UDP emulations */ +#define EMU_TALK 0x1 +#define EMU_NTALK 0x2 +#define EMU_CUSEEME 0x3 + + +struct tos_t { + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; +}; + + +extern u_int curtime, detach_time, detach_wait; + + +extern void slirp_insque(void *, void *); +extern void slirp_remque(void *); +extern void fd_nonblock(int); +extern void fd_block(int); + + +#endif /*SLIRP_MISC_H*/ diff --git a/src/poll.c b/src/poll.c new file mode 100644 index 0000000..57fed49 --- /dev/null +++ b/src/poll.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#ifdef _WIN32 +# include +# ifdef _MSC_VER +# include +# else +# include +# endif +#else +# include +# include +# include +# include +# include +# include +#endif +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "mbuf.h" +#include "if.h" +#include "socket.h" +#include "mbuf.h" +#include "ip.h" +#include "tcp.h" +#include "udp.h" + + +#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) +#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) +#define UPD_NFDS(x) if (nfds < (x)) nfds = (x) + + +u_int curtime; +fd_set *global_readfds, + *global_writefds, + *global_xfds; + + +/* curtime kept to an accuracy of 1ms */ +static void +updtime(void) +{ +#ifdef _WIN32 + struct _timeb tb; + + _ftime(&tb); + curtime = (u_int)tb.time * (u_int)1000; + curtime += (u_int)tb.millitm; +#else + struct timeval tt; + + gettimeofday(&tt, 0); + + curtime = (u_int)tt.tv_sec * (u_int)1000; + curtime += (u_int)tt.tv_usec / (u_int)1000; + + if ((tt.tv_usec % 1000) >= 500) + curtime++; +#endif +} + + +static int +poll_fill(slirp_t *slirp, int *pnfds, fd_set *rfds, fd_set *wfds, fd_set *xfds) +{ + struct SLIRPsocket *so, *so_next; + int nfds, tmo, tmp_time; + + /* fail safe */ + global_readfds = NULL; + global_writefds = NULL; + global_xfds = NULL; + + nfds = *pnfds; + + /* First, TCP sockets. */ + slirp->do_slowtimo = 0; + if (slirp->link_up) { + /* + * *_slowtimo needs calling if there are IP fragments + * in the fragment queue, or there are TCP connections active + */ + slirp->do_slowtimo = ((tcb.so_next != &tcb) || + ((struct ipasfrag *)&ipq != (struct ipasfrag *)ipq.next)); + + for (so = tcb.so_next; (so != &tcb); so = so_next) { + so_next = so->so_next; + + /* See if we need a tcp_fasttimo. */ + if (so->so_tcpcb != 0) { + /* This is to prevent a common lockup. */ + if (slirp->time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) + /* Flag when we want a fasttimo */ + slirp->time_fasttimo = curtime; + } + + /* + * NOFDREF can include still connecting to local-host, + * newly socreated() sockets etc. Don't want to select + * these. + */ + if (so->so_state & SS_NOFDREF || so->s == -1) + continue; + + /* Set for reading sockets which are accepting. */ + if (so->so_state & SS_FACCEPTCONN) { + FD_SET(so->s, rfds); + UPD_NFDS(so->s); + continue; + } + + /* Set for writing sockets which are connecting. */ + if (so->so_state & SS_ISFCONNECTING) { + FD_SET(so->s, wfds); + UPD_NFDS(so->s); + continue; + } + + /* + * Set for writing if we are connected, can send more, + * and we have something to send. + */ + if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { + FD_SET(so->s, wfds); + UPD_NFDS(so->s); + } + + /* + * Set for reading (and urgent data) if we are + * connected, can receive more, and we have room + * for it XXX /2 ? + */ + if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { + FD_SET(so->s, rfds); + FD_SET(so->s, xfds); + UPD_NFDS(so->s); + } + } + + /* UDP sockets. */ + for (so = udb.so_next; so != &udb; so = so_next) { + so_next = so->so_next; + + /* See if it's timed out. */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + udp_detach(so); + continue; + } else + slirp->do_slowtimo = 1; /* Let socket expire */ + } + + /* + * When UDP packets are received from over the + * link, they're sendto()'d straight away, so + * no need for setting for writing + * Limit the number of packets queued by this session + * to 4. Note that even though we try and limit this + * to 4 packets, the session could have more queued + * if the packets needed to be fragmented + * (XXX <= 4 ?) + */ + if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { + FD_SET(so->s, rfds); + UPD_NFDS(so->s); + } + } + } + + /* Setup timeout to use minimum CPU usage, especially when idle. */ + tmo = -1; + + /* + * If a slowtimo is needed, set timeout to 5ms from the last + * slow timeout. If a fast timeout is needed, set timeout within + * 2ms of when it was requested. + */ +#define SLOW_TIMO 5 +#define FAST_TIMO 2 + if (slirp->do_slowtimo) { + tmo = (SLOW_TIMO - (curtime - slirp->last_slowtimo)) * 1000; + if (tmo < 0) + tmo = 0; + else if (tmo > (SLOW_TIMO * 1000)) + tmo = SLOW_TIMO * 1000; + + /* Can only fasttimo if we also slowtimo */ + if (slirp->time_fasttimo) { + tmp_time = (FAST_TIMO - (curtime - slirp->time_fasttimo)) * 1000; + if (tmp_time < 0) + tmp_time = 0; + + /* Choose the smallest of the 2 */ + if (tmp_time < tmo) + tmo = tmp_time; + } + } + *pnfds = nfds; + + /* + * Adjust the timeout to make the minimum timeout + * 2ms (XXX?) to lessen the CPU load + */ + if (tmo < (FAST_TIMO * 1000)) + tmo = FAST_TIMO * 1000; + + return tmo; +} + + +static void +poll_select(slirp_t *slirp, fd_set *rfds, fd_set *wfds, fd_set *xfds) +{ + struct SLIRPsocket *so, *so_next; + int ret; + + global_readfds = rfds; + global_writefds = wfds; + global_xfds = xfds; + + /* Update time */ + updtime(); + + /* See if anything has timed out. */ + if (slirp->link_up) { + if (slirp->time_fasttimo && ((curtime - slirp->time_fasttimo) >= FAST_TIMO)) { + tcp_fasttimo(); + slirp->time_fasttimo = 0; + } + + if (slirp->do_slowtimo && ((curtime - slirp->last_slowtimo) >= SLOW_TIMO)) { + ip_slowtimo(); + tcp_slowtimo(); + slirp->last_slowtimo = curtime; + } + } + + /* Check sockets. */ + if (slirp->link_up) { + /* Check TCP sockets. */ + for (so = tcb.so_next; so != &tcb; so = so_next) { + so_next = so->so_next; + + /* + * FD_ISSET is meaningless on these sockets + * (and they can crash the program) + */ + if (so->so_state & SS_NOFDREF || so->s == -1) + continue; + + /* + * Check for URG data + * This will soread as well, so no need to + * test for readfds below if this succeeds + */ + if (FD_ISSET(so->s, xfds)) + sorecvoob(so); + + /* Check sockets for reading. */ + else if (FD_ISSET(so->s, rfds)) { + /* Check for incoming connections. */ + if (so->so_state & SS_FACCEPTCONN) { + tcp_connect(so); + continue; + } /* else */ + ret = soread(so); + + /* Output it if we read something */ + if (ret > 0) + tcp_output(sototcpcb(so)); + } + + /* Check sockets for writing. */ + if (FD_ISSET(so->s, wfds)) { + /* Check for non-blocking, still-connecting sockets. */ + if (so->so_state & SS_ISFCONNECTING) { + /* Connected */ + so->so_state &= ~SS_ISFCONNECTING; + } + } + + /* + * Now UDP sockets. + * Incoming packets are sent straight away, they're not + * buffered. Incoming UDP data isn't buffered either. + */ + for (so = udb.so_next; so != &udb; so = so_next) { + so_next = so->so_next; + + if (so->s != -1 && FD_ISSET(so->s, rfds)) { + sorecvfrom(so); + } + } + } + } + + /* See if we can start outputting. */ + if (if_queued && slirp->link_up) + if_start(); + + /* Clear global file descriptor sets. */ + global_readfds = NULL; + global_writefds = NULL; + global_xfds = NULL; +} + + +/* API: poll an instance for any work. */ +int +slirp_poll(slirp_t *slirp) +{ + fd_set rfds, wfds, xfds; + struct timeval tv; + int ret, nfds, tmo; + + /* Create a list of all open sockets. */ + nfds = -1; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + tmo = poll_fill(slirp, &nfds, &rfds, &wfds, &xfds); + if (tmo < 0) + tmo = 500; + + tv.tv_sec = 0; + tv.tv_usec = tmo; + + /* Now wait for something to happen, or at most 'tmo' usec. */ + ret = select(nfds+1, &rfds, &wfds, &xfds, &tv); + if (ret < 0) + return -1; + + /* If something happened, let SLiRP handle it. */ + if (ret >= 0) + poll_select(slirp, &rfds, &wfds, &xfds); + + return ret; +} diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..a0570ed --- /dev/null +++ b/src/queue.c @@ -0,0 +1,115 @@ +/* + * File: queue.c + * Author: Robert I. Pitts + * Last Modified: March 9, 2000 + * Topic: Queue - Array Implementation + * ---------------------------------------------------------------- + */ +#include +#include +#include "queue.h" + +/* + * Constants + * --------- + * MAX_QUEUE_SIZE = Largest number of items queue can hold. + */ + +#define MAX_QUEUE_SIZE 100 + +/* + * struct queueCDT gives the implementation of a queue. + * It holds the information that we need for each queue. + */ +typedef struct queueCDT { + queueElementT contents[MAX_QUEUE_SIZE]; + int front; + int count; +} queueCDT; + +queueADT QueueCreate(void) +{ + queueADT queue; + + queue = (queueADT)malloc(sizeof(queueCDT)); + + if (queue == NULL) { + fprintf(stderr, "Insufficient Memory for new Queue.\n"); + exit(ERROR_MEMORY); /* Exit program, returning error code. */ + } + + queue->front = 0; + queue->count = 0; + + return queue; +} + +void QueueDestroy(queueADT queue) +{ + free(queue); +} + +void QueueEnter(queueADT queue, queueElementT element) +{ + int newElementIndex; + + if (queue->count >= MAX_QUEUE_SIZE) { +// fprintf(stderr, "QueueEnter on Full Queue.\n"); +// exit(ERROR_QUEUE); /* Exit program, returning error code. */ + return; + } + + /* + * Calculate index at which to put + * next element. + */ + newElementIndex = (queue->front + queue->count) + % MAX_QUEUE_SIZE; + queue->contents[newElementIndex] = element; +//printf("element %d, pointer to %d, [%s]\n",newElementIndex,element,element); + + queue->count++; +} + +int QueuePeek(queueADT queue) +{ +return queue->count; +} + +queueElementT QueueDelete(queueADT queue) +{ + queueElementT oldElement; + + if (queue->count <= 0) { + //fprintf(stderr, "QueueDelete on Empty Queue.\n"); + //exit(ERROR_QUEUE); /* Exit program, returning error code. */ + return NULL; + } + + /* Save the element so we can return it. */ + oldElement = queue->contents[queue->front]; + + /* + * Advance the index of the front, + * making sure it wraps around the + * array properly. + */ + queue->front++; + queue->front %= MAX_QUEUE_SIZE; + +//printf("dequing @%d [%s]\n",oldElement,oldElement); + + queue->count--; + + return oldElement; +} + +int QueueIsEmpty(queueADT queue) +{ + return queue->count <= 0; +} + +int QueueIsFull(queueADT queue) +{ + return queue->count >= MAX_QUEUE_SIZE; +} diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000..786950a --- /dev/null +++ b/src/queue.h @@ -0,0 +1,101 @@ +/* + * File: queue.h + * Author: Robert I. Pitts + * Last Modified: March 9, 2000 + * Topic: Queue - Array Implementation + * ---------------------------------------------------------------- + */ + +#ifndef _QUEUE_H +#define _QUEUE_H + +/* + * Constants + * --------- + * ERROR_* These signal error conditions in queue functions + * and are used as exit codes for the program. + */ +#define ERROR_QUEUE 2 +#define ERROR_MEMORY 3 + +/* + * Type: queueElementT + * ------------------- + * This is the type of objects held in the queue. + */ + +/*typedef char queueElementT; +typedef unsigned char *queueElementT; +*/ + +struct queuepacket{ + int len; + unsigned char data[2000]; +}; +typedef struct queuepacket *queueElementT; + +/* + * Type: queueADT + * -------------- + * The actual implementation of a queue is completely + * hidden. Client will work with queueADT which is a + * pointer to underlying queueCDT. + */ + +/* + * NOTE: need word struct below so that the compiler + * knows at least that a queueCDT will be some sort + * of struct. + */ + +typedef struct queueCDT *queueADT; + +/* + * Function: QueueCreate + * Usage: queue = QueueCreate(); + * ------------------------- + * A new empty queue is created and returned. + */ + +queueADT QueueCreate(void); + +/* Function: QueueDestroy + * Usage: QueueDestroy(queue); + * ----------------------- + * This function frees all memory associated with + * the queue. "queue" may not be used again unless + * queue = QueueCreate() is called first. + */ + +void QueueDestroy(queueADT queue); + +/* + * Functions: QueueEnter, QueueDelete + * Usage: QueueEnter(queue, element); + * element = QueueDelete(queue); + * -------------------------------------------- + * These are the fundamental queue operations that enter + * elements in and delete elements from the queue. A call + * to QueueDelete() on an empty queue or to QueueEnter() + * on a full queue is an error. Make use of QueueIsFull() + * and QueueIsEmpty() (see below) to avoid these errors. + */ + +void QueueEnter(queueADT queue, queueElementT element); +queueElementT QueueDelete(queueADT queue); + + +/* + * Functions: QueueIsEmpty, QueueIsFull + * Usage: if (QueueIsEmpty(queue)) ... + * ----------------------------------- + * These return a true/false value based on whether + * the queue is empty or full, respectively. + */ + +int QueueIsEmpty(queueADT queue); +int QueueIsFull(queueADT queue); + +int QueuePeek(queueADT queue); + +#endif /* not defined _QUEUE_H */ diff --git a/src/sbuf.c b/src/sbuf.c new file mode 100644 index 0000000..618740e --- /dev/null +++ b/src/sbuf.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" + + +/* Done as a macro in socket.h */ +/* int + * sbspace(struct sockbuff *sb) + * { + * return SB_DATALEN - sb->sb_cc; + * } + */ + +void +sbfree(struct sbuf *sb) +{ + free(sb->sb_data); +} + +void +sbdrop(struct sbuf *sb, int num) +{ + /* + * We can only drop how much we have + * This should never succeed + */ + if (num > (int)sb->sb_cc) + num = sb->sb_cc; + sb->sb_cc -= num; + sb->sb_rptr += num; + if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen) + sb->sb_rptr -= sb->sb_datalen; + +} + +void +sbreserve(struct sbuf *sb, int size) +{ + if (sb->sb_data) { + /* Already alloced, realloc if necessary */ + if (sb->sb_datalen != size) { + sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size); + sb->sb_cc = 0; + if (sb->sb_wptr) + sb->sb_datalen = size; + else + sb->sb_datalen = 0; + } + } else { + sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size); + sb->sb_cc = 0; + if (sb->sb_wptr) + sb->sb_datalen = size; + else + sb->sb_datalen = 0; + } +} + +/* + * Try and write() to the socket, whatever doesn't get written + * append to the buffer... for a host with a fast net connection, + * this prevents an unnecessary copy of the data + * (the socket is non-blocking, so we won't hang) + */ +void +sbappend(struct SLIRPsocket *so, struct SLIRPmbuf *m) +{ + int ret = 0; + + DEBUG_CALL("sbappend"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m->m_len = %d", m->m_len); + + /* Shouldn't happen, but... e.g. foreign host closes connection */ + if (m->m_len <= 0) { + m_free(m); + return; + } + + /* + * If there is urgent data, call sosendoob + * if not all was sent, sowrite will take care of the rest + * (The rest of this function is just an optimisation) + */ + if (so->so_urgc) { + sbappendsb(&so->so_rcv, m); + m_free(m); + sosendoob(so); + return; + } + + /* + * We only write if there's nothing in the buffer, + * ottherwise it'll arrive out of order, and hence corrupt + */ + if (!so->so_rcv.sb_cc) + ret = send(so->s, m->m_data, m->m_len, 0); + + if (ret <= 0) { + /* + * Nothing was written + * It's possible that the socket has closed, but + * we don't need to check because if it has closed, + * it will be detected in the normal way by soread() + */ + sbappendsb(&so->so_rcv, m); + } else if (ret != m->m_len) { + /* + * Something was written, but not everything.. + * sbappendsb the rest + */ + m->m_len -= ret; + m->m_data += ret; + sbappendsb(&so->so_rcv, m); + } /* else */ + /* Whatever happened, we free the SLIRPmbuf */ + m_free(m); +} + +/* + * Copy the data from m into sb + * The caller is responsible to make sure there's enough room + */ +void +sbappendsb(struct sbuf *sb, struct SLIRPmbuf *m) +{ + int len, n, nn; + + len = m->m_len; + + if (sb->sb_wptr < sb->sb_rptr) { + n = sb->sb_rptr - sb->sb_wptr; + if (n > len) n = len; + memcpy(sb->sb_wptr, m->m_data, n); + } else { + /* Do the right edge first */ + n = sb->sb_data + sb->sb_datalen - sb->sb_wptr; + if (n > len) n = len; + memcpy(sb->sb_wptr, m->m_data, n); + len -= n; + if (len) { + /* Now the left edge */ + nn = sb->sb_rptr - sb->sb_data; + if (nn > len) nn = len; + memcpy(sb->sb_data,m->m_data+n,nn); + n += nn; + } + } + + sb->sb_cc += n; + sb->sb_wptr += n; + if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen) + sb->sb_wptr -= sb->sb_datalen; +} + +/* + * Copy data from sbuf to a normal, straight buffer + * Don't update the sbuf rptr, this will be + * done in sbdrop when the data is acked + */ +void +sbcopy(struct sbuf *sb, int off, int len, char *to) +{ + char *from; + + from = sb->sb_rptr + off; + if (from >= sb->sb_data + sb->sb_datalen) + from -= sb->sb_datalen; + + if (from < sb->sb_wptr) { + if (len > (int)sb->sb_cc) len = sb->sb_cc; + memcpy(to,from,len); + } else { + /* re-use off */ + off = (sb->sb_data + sb->sb_datalen) - from; + if (off > len) off = len; + memcpy(to,from,off); + len -= off; + if (len) + memcpy(to+off,sb->sb_data,len); + } +} + diff --git a/src/slirp.c b/src/slirp.c new file mode 100644 index 0000000..8af73d6 --- /dev/null +++ b/src/slirp.c @@ -0,0 +1,294 @@ +#ifdef _WIN32 +# include /* on top otherwise, it'll pull in winsock1 */ +#else +# include +# include +# include +#endif +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "host.h" +#include "mbuf.h" +#include "if.h" +#include "socket.h" +#include "arp.h" +#include "ip.h" +#ifdef USE_REDIR +# include "redir.h" +#endif + + +/* Our actual addresses. */ +char slirp_hostname[32]; +char slirp_domainname[128]; +struct in_addr our_addr; /* host IP address */ +struct in_addr dns_addr; /* host DNS server */ +struct in_addr loopback_addr; /* host loopback address */ + +/* Our virtual addresses. */ +const uint8_t special_macaddr[ETH_ALEN] = { + 0x52, 0x54, 0x00, 0x12, 0x35, 0x00 /* virtual MAC address. */ +}; +struct in_addr special_addr; /* virtual IP address */ +struct in_addr router_addr; /* virtual address alias for router */ +struct in_addr server_addr; /* virtual address alias for server */ +//struct in_addr alias_addr; /* virtual address alias for host */ +//struct in_addr myaddr; + + +uint8_t client_macaddr[ETH_ALEN]; /* guest's MAC address */ +int link_up; +u_int detach_time; +u_int detach_wait = 600000; /* 10 minutes */ + + +/* API: return the library version. */ +int +slirp_version(char *bufp, int max_len) +{ + char temp[128]; + + sprintf(temp, "%s", SLIRP_VERSION); + + strncpy(bufp, temp, max_len); + + return(strlen(temp)); +} + + +/* API: initialize an instance for use. */ +slirp_t * +slirp_init(void) +{ + slirp_t *slirp; +#if 0 + char *sp; +#endif + + /* First of all, initialize any host stuff. */ + if (host_init() != 0) return(NULL); + + /* Allocate an instance, and initialize it. */ + slirp = (slirp_t *)malloc(sizeof(slirp_t)); + if (slirp == NULL) + return(NULL); + memset(slirp, 0x00, sizeof(slirp_t)); + slirp->instance = 1; + slirp->link_up = 1; + + if_init(slirp); + ip_init(); + + /* Initialize mbufs *after* setting the MTU. */ + m_init(); + + /* Set default addresses. */ + memset(&our_addr, 0x00, sizeof(our_addr)); + memset(&dns_addr, 0x00, sizeof(dns_addr)); + inet_aton("127.0.0.1", &loopback_addr); + + /* Get local hostname and domainname. */ + memset(slirp_hostname, 0, sizeof(slirp_hostname)); + memset(slirp_domainname, 0, sizeof(slirp_domainname)); +#if 0 + if (! gethostname(temp, sizeof(temp) - 1)) { + /* We got the hostname. Do an IP lookup on it. */ + hp = gethostbyname(temp); + if (hp != NULL) + strncpy(temp, hp->h_name, sizeof(temp)); + + sp = strchr(temp, '.'); + if (sp != NULL) + *sp++ = '\0'; + + strncpy(slirp_hostname, temp, sizeof(slirp_hostname)); + strcat(slirp_hostname, "_slirp"); + + if (sp != NULL) { + strncpy(slirp_domainname, sp, sizeof(slirp_domainname)); + strcat(slirp_hostname, "."); + strcat(slirp_hostname, slirp_domainname); + } + } +#endif + + /* Get the host's network information. */ + if (host_get_info(&dns_addr, slirp_hostname, slirp_domainname) < 0) { + free(slirp); + return NULL; + } + + /* + * Initialize the virtual network. + * + * The default network configuration is: + * + * Network: 10.0.2.0/24 (255.255.255.0) + * Router: 10.0.2.1 + * DHCP/DNS: 10.0.2.2 + * Local: 10.0.2.15 (this may be removed later) + */ + inet_aton(CTL_SPECIAL, &special_addr); + router_addr.s_addr = special_addr.s_addr | htonl(CTL_ROUTER); + server_addr.s_addr = special_addr.s_addr | htonl(CTL_SERVER); +#ifdef CTL_LOCAL + myaddr.s_addr = special_addr.s_addr | htonl(CTL_LOCAL); +#endif + +#ifdef USE_REDIR + redir_init(); +#endif + + return slirp; +} + + +/* API: close an instance, and release all resources. */ +void +slirp_close(slirp_t *slirp) +{ + if (slirp != NULL) + free(slirp); + + /* Close down any host stuff. */ + host_close(); +} + + +/* API: receive a packet from the inbound queue. */ +int +slirp_recv(slirp_t *slirp, uint8_t *bufp) +{ + return 0; +} + + +/* API: send a packet to the outbound interface. */ +int +slirp_send(slirp_t *slirp, uint8_t *bufp, int pkt_len) +{ + return 0; +} + + +void +slirp_input(const uint8_t *pkt, int pkt_len) +{ + struct SLIRPmbuf *m; + int proto; + + if (pkt_len < ETH_HLEN) + return; + + proto = (pkt[12] << 8) | pkt[13]; + switch(proto) { + case ETH_P_ARP: + arp_input(pkt, pkt_len); + break; + + case ETH_P_IP: + m = m_get(); + if (!m) + return; + /* Note: we add to align the IP header */ + m->m_len = pkt_len + 2; + memcpy(m->m_data + 2, pkt, pkt_len); + + m->m_data += 2 + ETH_HLEN; + m->m_len -= 2 + ETH_HLEN; + + ip_input(m); + break; + + default: + break; + } +} + + +/* Output the IP packet to the client device. */ +void +if_encap(const uint8_t *ip_data, int ip_data_len) +{ + uint8_t buff[1600]; + struct ethhdr *eh = (struct ethhdr *)buff; + + if (ip_data_len + ETH_HLEN > sizeof(buff)) + return; + + /* Set the client device's MAC address. */ + memcpy(eh->h_dest, client_macaddr, ETH_ALEN); + + /* Set our MAC address. */ + memcpy(eh->h_source, special_macaddr, ETH_ALEN - 1); + eh->h_source[5] = CTL_SERVER; + + /* This works for IP, but not NetWare and such. */ + eh->h_proto = htons(ETH_P_IP); + + /* Now copy in the data to be sent. */ + memcpy(buff + ETH_HLEN, ip_data, ip_data_len); + ip_data_len += ETH_HLEN; + + slirp_output(buff, ip_data_len); +} + + +static void +purgesocks(void) +{ + struct SLIRPsocket *so; + + for (so = tcb.so_next; so != &tcb; so = so->so_next) + closesocket(so->s); //close the socket +} + + +void +slirp_exit(int exit_status) +{ + DEBUG_CALL("slirp_exit"); + DEBUG_ARG("exit_status = %d", exit_status); + + if (dostats) { + ipstats(); + tcpstats(); + udpstats(); + icmpstats(); + mbufstats(); + sockstats(); + fclose(dfd); + } + + purgesocks(); +} + + +/* Needed by SLiRP library. */ +void +slirp_output(const uint8_t *pkt, int pkt_len) +{ +#if 0 + struct queuepacket *qp; + + if (slirpq != NULL) { + qp = (struct queuepacket *)mem_alloc(sizeof(struct queuepacket)); + qp->len = pkt_len; + memcpy(qp->data, pkt, pkt_len); + QueueEnter(slirpq, qp); + } +#endif +} + + +/* Needed by SLiRP library. */ +int +slirp_can_output(void) +{ +// return((slirp != NULL)?1:0); + return 0; +} diff --git a/src/slirp.h b/src/slirp.h new file mode 100644 index 0000000..2cbe3f0 --- /dev/null +++ b/src/slirp.h @@ -0,0 +1,133 @@ +#ifndef SLIRP_H +# define SLIRP_H + +# include "config.h" + + +#define SLIRP_VERSION "1.0.9" + +#define CTL_SPECIAL "10.222.2.0" + +//#define CTL_CMD 0 +#define CTL_ROUTER 1 +#define CTL_SERVER 2 /* handled BOOTP/DHCP and DNS */ +//#define CTL_EXEC 4 +//#define CTL_LOCAL 15 + + +/* Define to 1 if you want KEEPALIVE timers */ +#define DO_KEEPALIVE 0 /* tcp_timer.c */ + +#define MIN_MRU 128 +#define MAX_MRU 16384 + + + +/* Avoid conflicting with the libc insque() and remque(), which + have different prototypes. */ +#ifdef insque +# undef insque +#endif +#define insque slirp_insque +#ifdef remque +# undef remque +#endif +#define remque slirp_remque + +#if SIZEOF_CHAR_P == 4 +# define insque_32 insque +# define remque_32 remque +#else +# ifdef NEED_QUE32_INLINE +extern __inline void insque_32(void *, void *); +extern __inline void remque_32(void *); +# else +extern void insque_32(void *, void *); +extern void remque_32(void *); +# endif +#endif + +#ifndef _WIN32 +# define min(x,y) ((x) < (y) ? (x) : (y)) +# define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int instance; + int link_up; + + /* poll.c */ + int do_slowtimo; + u_int time_fasttimo, + last_slowtimo; +} slirp_t; + +typedef void (*log_func_t)(slirp_t *, const char *, ...); + + +extern char slirp_hostname[32]; +extern char slirp_domainname[128]; + +extern uint8_t client_macaddr[6]; +extern const uint8_t special_macaddr[]; +extern int link_up; +extern u_int curtime; + +#ifdef HAVE_SOCKET_H +extern fd_set *global_readfds, *global_writefds, *global_xfds; +#endif + +#ifdef HAVE_INADDR_H +extern struct in_addr our_addr; +extern struct in_addr loopback_addr; +extern struct in_addr dns_addr; + +extern struct in_addr special_addr; +extern struct in_addr router_addr; +extern struct in_addr ctl_addr; +#endif + +extern int towrite_max; +extern int so_options; +extern int tcp_keepintvl; + +extern int do_echo; + + +/* stats.c */ +extern void mbufstats(void); +extern void sockstats(void); +extern void ipstats(void); +extern void icmpstats(void); +extern void tcpstats(void); +extern void udpstats(void); + +extern int slirp_version(char *bufp, int max_len); + +extern slirp_t *slirp_init(void); +extern void slirp_close(slirp_t *); + +extern int slirp_poll(slirp_t *slirp); + +#ifdef USE_REDIR +extern int slirp_redir(int is_udp, int host_port, + struct in_addr guest_addr, int guest_port); +extern int slirp_add_exec(int do_pty, const char *args, + int addr_low_byte, int guest_port); +#endif + +extern int slirp_can_output(void); +extern void slirp_output(const uint8_t *pkt, int pkt_len); +extern void slirp_input(const uint8_t *pkt, int pkt_len); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..7a8adac --- /dev/null +++ b/src/socket.c @@ -0,0 +1,769 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifdef _WIN32 +# include +# ifdef _DEBUG +# undef errno +# define errno (WSAGetLastError()) +# endif +#else +# include +# include +# include +# include +# ifdef _DEBUG +# include +# endif +#endif +#include +#include +#include +#include +#include +#ifndef FIONREAD +# include +#endif +#define HAVE_SOCKET_H +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" +#include "ip_icmp.h" +#include "tcp.h" +#include "udp.h" + + +void +so_init(void) +{ + /* Nothing yet */ +} + + +struct SLIRPsocket * +solookup(struct SLIRPsocket *head, struct in_addr laddr, u_int lport, + struct in_addr faddr, u_int fport) +{ + struct SLIRPsocket *so; + + for (so = head->so_next; so != head; so = so->so_next) { + if (so->so_lport == lport && so->so_laddr.s_addr == laddr.s_addr && + so->so_faddr.s_addr == faddr.s_addr && so->so_fport == fport) + break; + } + + if (so == head) + return (struct SLIRPsocket *)NULL; + + return so; + +} + + +/* + * Create a new socket, initialize the fields. + * It is the responsibility of the caller to + * insque() it into the correct linked-list + */ +struct SLIRPsocket * +socreate(void) +{ + struct SLIRPsocket *so; + + so = (struct SLIRPsocket *)malloc(sizeof(struct SLIRPsocket)); + if (so) { + memset(so, 0, sizeof(struct SLIRPsocket)); + so->so_state = SS_NOFDREF; + so->s = -1; + } + + return(so); +} + + +/* remque and free a socket, clobber cache */ +void +sofree(struct SLIRPsocket *so) +{ + if (so->so_emu==EMU_RSH && so->extra) { + sofree((struct SLIRPsocket *)so->extra); + so->extra = NULL; + } + + if (so == tcp_last_so) + tcp_last_so = &tcb; + else if (so == udp_last_so) + udp_last_so = &udb; + + if (so->so_m != NULL) + m_free(so->so_m); + + if (so->so_next && so->so_prev) + remque(so); /* crashes if so is not in a queue */ + + free(so); +} + + +/* + * Read from so's socket into sb_snd, updating all relevant sbuf fields + * NOTE: This will only be called if it is select()ed for reading, so + * a read() of 0 (or less) means it's disconnected + */ +int +soread(struct SLIRPsocket *so) +{ + struct sbuf *sb = &so->so_snd; + struct iovec iov[2]; + unsigned int len, mss; + unsigned int lss, total; + int n, nn, ret; + + if (! so->so_tcpcb) + return 0; + + len = sb->sb_datalen - sb->sb_cc; + mss = so->so_tcpcb->t_maxseg; + + DEBUG_CALL("soread"); + DEBUG_ARG("so = %lx", (long )so); + + /* + * No need to check if there's enough room to read. + * soread wouldn't have been called if there weren't + */ + len = sb->sb_datalen - sb->sb_cc; + + iov[0].iov_base = sb->sb_wptr; + if (sb->sb_wptr < sb->sb_rptr) { + iov[0].iov_len = sb->sb_rptr - sb->sb_wptr; + + /* Should never succeed, but... */ + if (iov[0].iov_len > len) + iov[0].iov_len = len; + if (iov[0].iov_len > mss) + iov[0].iov_len -= iov[0].iov_len%mss; + n = 1; + } else { + iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr; + + /* Should never succeed, but... */ + if (iov[0].iov_len > len) + iov[0].iov_len = len; + len -= iov[0].iov_len; + if (len) { + iov[1].iov_base = sb->sb_data; + iov[1].iov_len = sb->sb_rptr - sb->sb_data; + if (iov[1].iov_len > len) + iov[1].iov_len = len; + total = iov[0].iov_len + iov[1].iov_len; + if (total > mss) { + lss = total%mss; + if (iov[1].iov_len > lss) { + iov[1].iov_len -= lss; + n = 2; + } else { + lss -= iov[1].iov_len; + iov[0].iov_len -= lss; + n = 1; + } + } else + n = 2; + } else { + if (iov[0].iov_len > mss) + iov[0].iov_len -= iov[0].iov_len%mss; + n = 1; + } + } + +#ifdef HAVE_READV + nn = readv(so->s, (struct iovec *)iov, n); + DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); +#else + nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); +#endif + if (nn <= 0) { + if (nn < 0 && (errno == EINTR || errno == EAGAIN)) + return 0; + else { + DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno))); + sofcantrcvmore(so); + tcp_sockclosed(sototcpcb(so)); + return -1; + } + } + +#ifndef HAVE_READV + /* + * If there was no error, try and read the second time round + * We read again if n = 2 (ie, there's another part of the buffer) + * and we read as much as we could in the first read + * We don't test for <= 0 this time, because there legitimately + * might not be any more data (since the socket is non-blocking), + * a close will be detected on next iteration. + * A return of -1 wont (shouldn't) happen, since it didn't happen above + */ + if (n == 2 && nn == iov[0].iov_len) { + ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0); + if (ret > 0) + nn += ret; + } + + DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); +#endif + + /* Update fields */ + sb->sb_cc += nn; + sb->sb_wptr += nn; + if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_wptr -= sb->sb_datalen; + + return nn; +} + + +/* + * Get urgent data + * + * When the socket is created, we set it SO_OOBINLINE, + * so when OOB data arrives, we soread() it and everything + * in the send buffer is sent as urgent data + */ +void +sorecvoob(struct SLIRPsocket *so) +{ + struct tcpcb *tp = sototcpcb(so); + + DEBUG_CALL("sorecvoob"); + DEBUG_ARG("so = %lx", (long)so); + + /* + * We take a guess at how much urgent data has arrived. + * In most situations, when urgent data arrives, the next + * read() should get all the urgent data. This guess will + * be wrong however if more data arrives just after the + * urgent data, or the read() doesn't return all the + * urgent data. + */ + soread(so); + tp->snd_up = tp->snd_una + so->so_snd.sb_cc; + tp->t_force = 1; + tcp_output(tp); + tp->t_force = 0; +} + + +/* + * Send urgent data + * There's a lot duplicated code here, but... + */ +int +sosendoob(struct SLIRPsocket *so) +{ + char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ + struct sbuf *sb = &so->so_rcv; + int n, len; + + DEBUG_CALL("sosendoob"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc); + + if (so->so_urgc > sizeof(buff)) + so->so_urgc = sizeof(buff); /* XXXX */ + + if (sb->sb_rptr < sb->sb_wptr) { + /* We can send it directly */ + n = send(so->s, sb->sb_rptr, so->so_urgc, MSG_OOB /*|MSG_DONTWAIT*/); + so->so_urgc -= n; + + DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); + } else { + /* + * Since there's no sendv or sendtov like writev, + * we must copy all data to a linear buffer then + * send it all + */ + len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + if (len > so->so_urgc) + len = so->so_urgc; + memcpy(buff, sb->sb_rptr, len); + so->so_urgc -= len; + if (so->so_urgc) { + n = sb->sb_wptr - sb->sb_data; + if (n > so->so_urgc) + n = so->so_urgc; + memcpy((buff + len), sb->sb_data, n); + so->so_urgc -= n; + len += n; + } + n = send(so->s, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ +#ifdef _DEBUG + if (n != len) + DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); +#endif + DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); + } + + sb->sb_cc -= n; + sb->sb_rptr += n; + if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_rptr -= sb->sb_datalen; + + return n; +} + + +/* + * Write data from so_rcv to so's socket, + * updating all sbuf field as necessary + */ +int +sowrite(struct SLIRPsocket *so) +{ + struct sbuf *sb = &so->so_rcv; + struct iovec iov[2]; + unsigned int len; + int n, nn, ret; + + DEBUG_CALL("sowrite"); + DEBUG_ARG("so = %lx", (long)so); + + len = sb->sb_cc; + + if (so->so_urgc) { + sosendoob(so); + if (sb->sb_cc == 0) + return 0; + } + + /* + * No need to check if there's something to write, + * sowrite wouldn't have been called otherwise + */ + len = sb->sb_cc; + + iov[0].iov_base = sb->sb_rptr; + if (sb->sb_rptr < sb->sb_wptr) { + iov[0].iov_len = sb->sb_wptr - sb->sb_rptr; + /* Should never succeed, but... */ + if (iov[0].iov_len > len) iov[0].iov_len = len; + n = 1; + } else { + iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + if (iov[0].iov_len > len) iov[0].iov_len = len; + len -= iov[0].iov_len; + if (len) { + iov[1].iov_base = sb->sb_data; + iov[1].iov_len = sb->sb_wptr - sb->sb_data; + if (iov[1].iov_len > len) iov[1].iov_len = len; + n = 2; + } else + n = 1; + } + /* Check if there's urgent data to send, and if so, send it */ + +#ifdef HAVE_READV + nn = writev(so->s, (const struct iovec *)iov, n); + + DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); +#else + nn = send(so->s, iov[0].iov_base, iov[0].iov_len,0); +#endif + + /* This should never happen, but people tell me it does *shrug* */ + if (nn < 0 && (errno == EAGAIN || errno == EINTR)) + return 0; + + if (nn <= 0) { + DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n", + so->so_state, errno)); + sofcantsendmore(so); + tcp_sockclosed(sototcpcb(so)); + return -1; + } + +#ifndef HAVE_READV + if (n == 2 && nn == iov[0].iov_len) { + ret = send(so->s, iov[1].iov_base, iov[1].iov_len,0); + if (ret > 0) + nn += ret; + } + DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); +#endif + + /* Update sbuf */ + sb->sb_cc -= nn; + sb->sb_rptr += nn; + if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_rptr -= sb->sb_datalen; + + /* + * If in DRAIN mode, and there's no more data, set + * it CANTSENDMORE + */ + if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0) + sofcantsendmore(so); + + return nn; +} + + +/* recvfrom() a UDP socket */ +void +sorecvfrom(struct SLIRPsocket *so) +{ + char buff[256]; + struct sockaddr_in addr; + struct SLIRPmbuf *m; + socklen_t addrlen; + ioctlsockopt_t n; + u_char code; + int len; + + DEBUG_CALL("sorecvfrom"); + DEBUG_ARG("so = %lx", (long)so); + + addrlen = sizeof(struct sockaddr_in); + + if (so->so_type == IPPROTO_ICMP) { + /* This is a "ping" reply */ + len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen); + /* XXX Check if reply is "correct"? */ + + if (len == -1 || len == 0) { + code = ICMP_UNREACH_PORT; + + if (errno == EHOSTUNREACH) + code = ICMP_UNREACH_HOST; + else if (errno == ENETUNREACH) + code = ICMP_UNREACH_NET; + + DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n", + errno,strerror(errno))); + icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = 0; /* Don't m_free() it again! */ + } + + /* No need for this socket anymore, udp_detach it */ + udp_detach(so); + + return; + } + + /* A "normal" UDP packet */ + if (!(m = m_get())) return; + m->m_data += if_maxlinkhdr; + + /* + * XXX Shouldn't FIONREAD packets destined for port 53, + * but I don't know the max packet size for DNS lookups + */ + len = M_FREEROOM(m); + + /* if (so->so_fport != htons(53)) { */ + ioctlsocket(so->s, FIONREAD, &n); + if ((int)n > len) { + n = (m->m_data - m->m_dat) + m->m_len + n + 1; + m_inc(m, n); + len = M_FREEROOM(m); + } + /* } */ + + m->m_len = recvfrom(so->s, m->m_data, len, 0, + (struct sockaddr *)&addr, &addrlen); + DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", + m->m_len, errno,strerror(errno))); + + if (m->m_len < 0) { + code = ICMP_UNREACH_PORT; + + if (errno == EHOSTUNREACH) + code = ICMP_UNREACH_HOST; + else if (errno == ENETUNREACH) + code = ICMP_UNREACH_NET; + + DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code)); + icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); + m_free(m); + } else { + /* + * Hack: domain name lookup will be used the most for UDP, + * and since they'll only be used once there's no need + * for the 4 minute (or whatever) timeout... So we time them + * out much quicker (10 seconds for now...) + */ + if (so->so_expire) { + if (so->so_fport == htons(53)) + so->so_expire = curtime + SO_EXPIREFAST; + else + so->so_expire = curtime + SO_EXPIRE; + } + +#if 0 + if (m->m_len == len) { + m_inc(m, MINCSIZE); + m->m_len = 0; + } +#endif + + /* + * If this packet was destined for CTL_ADDR, + * make it look like that's where it came from, done by udp_output + */ + udp_output(so, m, &addr); + } +} + + +/* sendto() a socket */ +int +sosendto(struct SLIRPsocket *so, struct SLIRPmbuf *m) +{ + struct sockaddr_in addr; + int ret; + + DEBUG_CALL("sosendto"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + + addr.sin_family = AF_INET; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + /* It's an alias */ + switch(ntohl(so->so_faddr.s_addr) & 0xff) { + case CTL_SERVER: + addr.sin_addr = dns_addr; + break; + + case CTL_ROUTER: + default: + addr.sin_addr = loopback_addr; + break; + } + } else + addr.sin_addr = so->so_faddr; + addr.sin_port = so->so_fport; + + DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + + /* Don't care what port we get */ + ret = sendto(so->s, m->m_data, m->m_len, 0, + (struct sockaddr *)&addr, sizeof (struct sockaddr)); + if (ret < 0) + return -1; + + /* + * Kill the socket if there's no reply in 4 minutes, + * but only if it's an expirable socket + */ + if (so->so_expire) + so->so_expire = curtime + SO_EXPIRE; + so->so_state = SS_ISFCONNECTED; /* So that it gets select()ed */ + + return 0; +} + + +/* + * XXX This should really be tcp_listen + */ +struct SLIRPsocket * +solisten(u_int port, uint32_t laddr, u_int lport, int flags) +{ + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + struct SLIRPsocket *so; + int s, opt = 1; + int tmperrno; + + DEBUG_CALL("solisten"); + DEBUG_ARG("port = %d", port); + DEBUG_ARG("laddr = %x", laddr); + DEBUG_ARG("lport = %d", lport); + DEBUG_ARG("flags = %x", flags); + + if ((so = socreate()) == NULL) { + /* free(so); Not sofree() ??? free(NULL) == NOP */ + return NULL; + } + + /* Don't tcp_attach... we don't need so_snd nor so_rcv */ + if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) { + free(so); + return NULL; + } + insque(so,&tcb); + + /* SS_FACCEPTONCE sockets must time out. */ + if (flags & SS_FACCEPTONCE) + so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2; + + so->so_state = (SS_FACCEPTCONN|flags); + so->so_lport = lport; /* Kept in network format */ + so->so_laddr.s_addr = laddr; /* Ditto */ + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = port; + + if (((s = socket(AF_INET,SOCK_STREAM,0)) < 0) || + (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) || + (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || + (listen(s,1) < 0)) { + tmperrno = errno; /* Don't clobber the real reason we failed */ + +#ifdef _WIN32 + closesocket(s); +#else + close(s); +#endif + sofree(so); + + /* Restore the real errno */ +#ifdef _WIN32 + WSASetLastError(tmperrno); +#else + errno = tmperrno; +#endif + return NULL; + } + + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int)); + + getsockname(s,(struct sockaddr *)&addr,&addrlen); + so->so_fport = addr.sin_port; + if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) + so->so_faddr = router_addr; + else + so->so_faddr = addr.sin_addr; + + so->s = s; + + return so; +} + + +/* + * Data is available in so_rcv + * Just write() the data to the socket + * XXX not yet... + */ +void +sorwakeup(struct SLIRPsocket *so) +{ +#if 0 + sowrite(so); + FD_CLR(so->s, &writefds); +#endif +} + + +/* + * Data has been freed in so_snd + * We have room for a read() if we want to + * For now, don't read, it'll be done in the main loop + */ +void +sowwakeup(struct SLIRPsocket *so) +{ + /* Nothing, yet */ +} + + +/* + * Various session state calls + * XXX Should be #define's + * The socket state stuff needs work, these often get call 2 or 3 + * times each when only 1 was needed + */ +void +soisfconnecting(register struct SLIRPsocket *so) +{ + so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE| + SS_FCANTSENDMORE|SS_FWDRAIN); + so->so_state |= SS_ISFCONNECTING; /* Clobber other states */ +} + + +void +soisfconnected(register struct SLIRPsocket *so) +{ + so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF); + so->so_state |= SS_ISFCONNECTED; /* Clobber other states */ +} + + +void +sofcantrcvmore(struct SLIRPsocket *so) +{ + if ((so->so_state & SS_NOFDREF) == 0) { + shutdown(so->s, 0); + if (global_writefds) { + FD_CLR(so->s, global_writefds); + } + } + + so->so_state &= ~(SS_ISFCONNECTING); + if (so->so_state & SS_FCANTSENDMORE) + so->so_state = SS_NOFDREF; /* Don't select it */ /* XXX close() here as well? */ + else + so->so_state |= SS_FCANTRCVMORE; +} + + +void +sofcantsendmore(struct SLIRPsocket *so) +{ + if ((so->so_state & SS_NOFDREF) == 0) { + shutdown(so->s, 1); /* send FIN to fhost */ + if (global_readfds) { + FD_CLR(so->s, global_readfds); + } + if (global_xfds) { + FD_CLR(so->s,global_xfds); + } + } + + so->so_state &= ~(SS_ISFCONNECTING); + if (so->so_state & SS_FCANTRCVMORE) + so->so_state = SS_NOFDREF; /* as above */ + else + so->so_state |= SS_FCANTSENDMORE; +} + + +void +soisfdisconnected(struct SLIRPsocket *so) +{ +#if 0 + so->so_state &= ~(SS_ISFCONNECTING|SS_ISFCONNECTED); + close(so->s); + so->so_state = SS_ISFDISCONNECTED; +#endif + + /* XXX Do nothing ... ? */ +} + + +/* + * Set write drain mode + * Set CANTSENDMORE once all data has been write()n + */ +void +sofwdrain(struct SLIRPsocket *so) +{ + if (so->so_rcv.sb_cc) + so->so_state |= SS_FWDRAIN; + else + sofcantsendmore(so); +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..7603924 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifndef SLIRP_SOCKET_H +# define SLIRP_SOCKET_H + + +#define SO_EXPIRE 240000 +#define SO_EXPIREFAST 10000 + + +#define sbflush(sb) sbdrop((sb),(sb)->sb_cc) +#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc) + + +struct sbuf { + u_int sb_cc; /* actual chars in buffer */ + u_int sb_datalen; /* Length of data */ + char *sb_wptr; /* write pointer. points to where the next + * bytes should be written in the sbuf */ + char *sb_rptr; /* read pointer. points to where the next + * byte should be read from the sbuf */ + char *sb_data; /* Actual data */ +}; + + +/* + * Our socket structure + */ +struct SLIRPsocket { + struct SLIRPsocket *so_next, + *so_prev; /* For a linked list of sockets */ + + int s; /* The actual socket */ + + /* XXX union these with not-yet-used sbuf params */ + struct SLIRPmbuf *so_m; /* Pointer to the original SYN packet, + * for non-blocking connect()'s, and + * PING reply's */ + struct tcpiphdr *so_ti; /* Pointer to the original ti within + * so_mconn, for non-blocking connections */ + int so_urgc; + struct in_addr so_faddr; /* foreign host table entry */ + struct in_addr so_laddr; /* local host table entry */ + uint16_t so_fport; /* foreign port */ + uint16_t so_lport; /* local port */ + + uint8_t so_iptos; /* Type of service */ + uint8_t so_emu; /* Is the socket emulated? */ + + uint8_t so_type; /* Type of socket, UDP or TCP */ + int so_state; /* internal state flags SS_*, below */ + + struct tcpcb *so_tcpcb;/* pointer to TCP protocol control block */ + u_int so_expire; /* When the socket will expire */ + + int so_queued; /* Number of packets queued from this socket */ + int so_nqueued; /* Number of packets queued in a row + * Used to determine when to + * "downgrade" a session from fastq + * to batchq */ + + struct sbuf so_rcv; /* Receive buffer */ + struct sbuf so_snd; /* Send buffer */ + void *extra; /* Extra pointer */ +}; + + +/* + * Socket state bits. (peer means the host on the Internet, + * local host means the host on the other end of the modem) + */ +#define SS_NOFDREF 0x001 /* No fd reference */ + +#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */ +#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */ +#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */ +#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */ +/* #define SS_ISFDISCONNECTED 0x020*/ /* Socket has disconnected from peer, in 2MSL state */ +#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */ + +#define SS_CTL 0x080 +#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */ +#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */ + +extern struct SLIRPsocket tcb; + + +#if defined(DECLARE_IOVEC) && !defined(HAVE_READV) +struct iovec { + char *iov_base; + size_t iov_len; +}; +#endif + + +extern void sbfree(struct sbuf *); +extern void sbdrop(struct sbuf *, int); +extern void sbreserve(struct sbuf *, int); +extern void sbappend(struct SLIRPsocket *, struct SLIRPmbuf *); +extern void sbappendsb(struct sbuf *, struct SLIRPmbuf *); +extern void sbcopy(struct sbuf *, int, int, char *); + +extern void so_init(void); +extern struct SLIRPsocket *solookup(struct SLIRPsocket *, + struct in_addr, u_int, + struct in_addr, u_int); +extern struct SLIRPsocket *socreate(void); +extern void sofree(struct SLIRPsocket *); +extern int soread(struct SLIRPsocket *); +extern void sorecvoob(struct SLIRPsocket *); +extern int sosendoob(struct SLIRPsocket *); +extern int sowrite(struct SLIRPsocket *); +extern void sorecvfrom(struct SLIRPsocket *); +extern int sosendto(struct SLIRPsocket *, struct SLIRPmbuf *); +extern struct SLIRPsocket *solisten(u_int, uint32_t, u_int, int); +extern void sorwakeup(struct SLIRPsocket *); +extern void sowwakeup(struct SLIRPsocket *); +extern void soisfconnecting(register struct SLIRPsocket *); +extern void soisfconnected(register struct SLIRPsocket *); +extern void sofcantrcvmore(struct SLIRPsocket *); +extern void sofcantsendmore(struct SLIRPsocket *); +extern void soisfdisconnected(struct SLIRPsocket *); +extern void sofwdrain(struct SLIRPsocket *); + + +#endif /*SLIRP_SOCKET_H*/ diff --git a/src/stats.c b/src/stats.c new file mode 100644 index 0000000..4e2371d --- /dev/null +++ b/src/stats.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * Portions copyright (c) 2000 Kelly Price. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" +#include "ip_icmp.h" +#include "icmp_var.h" +#include "tcp.h" +#include "udp.h" + + +void +ipstats(void) +{ + lprint("\n"); + + lprint("IP stats:\n"); + lprint(" %6d total packets received (%d were unaligned)\n", + ipstat.ips_total, ipstat.ips_unaligned); + lprint(" %6d with incorrect version\n", ipstat.ips_badvers); + lprint(" %6d with bad header checksum\n", ipstat.ips_badsum); + lprint(" %6d with length too short (len < sizeof(iphdr))\n", + ipstat.ips_tooshort); + lprint(" %6d with length too small (len < ip->len)\n", + ipstat.ips_toosmall); + lprint(" %6d with bad header length\n", ipstat.ips_badhlen); + lprint(" %6d with bad packet length\n", ipstat.ips_badlen); + lprint(" %6d fragments received\n", ipstat.ips_fragments); + lprint(" %6d fragments dropped\n", ipstat.ips_fragdropped); + lprint(" %6d fragments timed out\n", ipstat.ips_fragtimeout); + lprint(" %6d packets reassembled ok\n", ipstat.ips_reassembled); + lprint(" %6d outgoing packets fragmented", ipstat.ips_fragmented); + lprint(" %6d total outgoing fragments\n", ipstat.ips_ofragments); + lprint(" %6d with bad protocol field\n", ipstat.ips_noproto); + lprint(" %6d total packets delivered\n", ipstat.ips_delivered); +} + + +void +icmpstats(void) +{ + lprint("\n"); + + lprint("ICMP stats:\n"); + lprint(" %6d ICMP packets received\n", icmpstat.icps_received); + lprint(" %6d were too short\n", icmpstat.icps_tooshort); + lprint(" %6d with bad checksums\n", icmpstat.icps_checksum); + lprint(" %6d with type not supported\n", icmpstat.icps_notsupp); + lprint(" %6d with bad type feilds\n", icmpstat.icps_badtype); + lprint(" %6d ICMP packets sent in reply\n", icmpstat.icps_reflect); +} + + +void +tcpstats(void) +{ + lprint("\n"); + + lprint("TCP stats:\n"); + lprint(" %6d packets sent\n", tcpstat.tcps_sndtotal); + lprint(" %6d data packets (%d bytes)\n", + tcpstat.tcps_sndpack, tcpstat.tcps_sndbyte); + lprint(" %6d data packets retransmitted (%d bytes)\n", + tcpstat.tcps_sndrexmitpack, tcpstat.tcps_sndrexmitbyte); + lprint(" %6d ack-only packets (%d delayed)\n", + tcpstat.tcps_sndacks, tcpstat.tcps_delack); + lprint(" %6d URG only packets\n", tcpstat.tcps_sndurg); + lprint(" %6d window probe packets\n", tcpstat.tcps_sndprobe); + lprint(" %6d window update packets\n", tcpstat.tcps_sndwinup); + lprint(" %6d control (SYN/FIN/RST) packets\n", + tcpstat.tcps_sndctrl); + lprint(" %6d times tcp_output did nothing\n", + tcpstat.tcps_didnuttin); + + lprint(" %6d packets received\n", tcpstat.tcps_rcvtotal); + lprint(" %6d acks (for %d bytes)\n", + tcpstat.tcps_rcvackpack, tcpstat.tcps_rcvackbyte); + lprint(" %6d duplicate acks\n", tcpstat.tcps_rcvdupack); + lprint(" %6d acks for unsent data\n", + tcpstat.tcps_rcvacktoomuch); + lprint(" %6d packets received in sequence (%d bytes)\n", + tcpstat.tcps_rcvpack, tcpstat.tcps_rcvbyte); + lprint(" %6d completely duplicate packets (%d bytes)\n", + tcpstat.tcps_rcvduppack, tcpstat.tcps_rcvdupbyte); + + lprint(" %6d packets with some duplicate data (%d bytes duped)\n", + tcpstat.tcps_rcvpartduppack, tcpstat.tcps_rcvpartdupbyte); + lprint(" %6d out-of-order packets (%d bytes)\n", + tcpstat.tcps_rcvoopack, tcpstat.tcps_rcvoobyte); + lprint(" %6d packets of data after window (%d bytes)\n", + tcpstat.tcps_rcvpackafterwin, tcpstat.tcps_rcvbyteafterwin); + lprint(" %6d window probes\n", tcpstat.tcps_rcvwinprobe); + lprint(" %6d window update packets\n", tcpstat.tcps_rcvwinupd); + lprint(" %6d packets received after close\n", + tcpstat.tcps_rcvafterclose); + lprint(" %6d discarded for bad checksums\n", + tcpstat.tcps_rcvbadsum); + lprint(" %6d discarded for bad header offset fields\n", + tcpstat.tcps_rcvbadoff); + + lprint(" %6d connection requests\n", tcpstat.tcps_connattempt); + lprint(" %6d connection accepts\n", tcpstat.tcps_accepts); + lprint(" %6d connections established (including accepts)\n", + tcpstat.tcps_connects); + lprint(" %6d connections closed (including %d drop)\n", + tcpstat.tcps_closed, tcpstat.tcps_drops); + lprint(" %6d embryonic connections dropped\n", tcpstat.tcps_conndrops); + lprint(" %6d segments we tried to get rtt (%d succeeded)\n", + tcpstat.tcps_segstimed, tcpstat.tcps_rttupdated); + lprint(" %6d retransmit timeouts\n", tcpstat.tcps_rexmttimeo); + lprint(" %6d connections dropped by rxmt timeout\n", + tcpstat.tcps_timeoutdrop); + lprint(" %6d persist timeouts\n", tcpstat.tcps_persisttimeo); + lprint(" %6d keepalive timeouts\n", tcpstat.tcps_keeptimeo); + lprint(" %6d keepalive probes sent\n", tcpstat.tcps_keepprobe); + lprint(" %6d connections dropped by keepalive\n", + tcpstat.tcps_keepdrops); + lprint(" %6d correct ACK header predictions\n", tcpstat.tcps_predack); + lprint(" %6d correct data packet header predictions\n", + tcpstat.tcps_preddat); + lprint(" %6d TCP cache misses\n", tcpstat.tcps_socachemiss); + +#if 0 + lprint(" Packets received too short: %d\n", tcpstat.tcps_rcvshort); + lprint(" Segments dropped due to PAWS: %d\n", + tcpstat.tcps_pawsdrop); +#endif +} + + +void +udpstats(void) +{ + lprint("\n"); + + lprint("UDP stats:\n"); + lprint(" %6d datagrams received\n", udpstat.udps_ipackets); + lprint(" %6d with packets shorter than header\n", udpstat.udps_hdrops); + lprint(" %6d with bad checksums\n", udpstat.udps_badsum); + lprint(" %6d with data length larger than packet\n", udpstat.udps_badlen); + lprint(" %6d UDP socket cache misses\n", udpstat.udpps_pcbcachemiss); + lprint(" %6d datagrams sent\n", udpstat.udps_opackets); +} + + +void +mbufstats(void) +{ + struct SLIRPmbuf *m; + int i; + + lprint("\n"); + + lprint("Mbuf stats:\n"); + lprint(" %6d mbufs allocated (%d max)\n", mbuf_alloced, mbuf_max); + + i = 0; + for (m = m_freelist.m_next; m != &m_freelist; m = m->m_next) + i++; + lprint(" %6d mbufs on free list\n", i); + + i = 0; + for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) + i++; + lprint(" %6d mbufs on used list\n", i); + + lprint(" %6d mbufs queued as packets\n\n", if_queued); +} + + +void +sockstats(void) +{ + char buff[256]; + struct SLIRPsocket *so; + int n; + + lprint("\n"); + + lprint("Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\n"); + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + n = sprintf(buff, "tcp[%s]", + so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE"); + while (n < 17) + buff[n++] = ' '; + buff[17] = 0; + lprint("%s %3d %15s %5d ", + buff, so->s, inet_ntoa(so->so_laddr), ntohs(so->so_lport)); + lprint("%15s %5d %5d %5d\n", + inet_ntoa(so->so_faddr), ntohs(so->so_fport), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } + + for (so = udb.so_next; so != &udb; so = so->so_next) { + n = sprintf(buff, "udp[%d sec]", (so->so_expire - curtime) / 1000); + while (n < 17) + buff[n++] = ' '; + buff[17] = 0; + lprint("%s %3d %15s %5d ", + buff, so->s, inet_ntoa(so->so_laddr), ntohs(so->so_lport)); + lprint("%15s %5d %5d %5d\r\n", + inet_ntoa(so->so_faddr), ntohs(so->so_fport), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } +} diff --git a/src/tcp.h b/src/tcp.h new file mode 100644 index 0000000..4e8da35 --- /dev/null +++ b/src/tcp.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp.h 8.1 (Berkeley) 6/10/93 + * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp + */ + +#ifndef _TCP_H_ +#define _TCP_H_ + +#ifdef __amd64__ +typedef uintptr_t tcp_seq; +#else +typedef uint32_t tcp_seq; +#endif + +#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ +#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ + +extern int tcp_rcvspace; +extern int tcp_sndspace; +extern struct SLIRPsocket *tcp_last_so; + +#define TCP_SNDSPACE 8192 +#define TCP_RCVSPACE 8192 + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif +struct tcphdr { + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +#ifdef WORDS_BIGENDIAN + uint8_t th_off:4, /* data offset */ + th_x2:4; /* (unused) */ +#else + uint8_t th_x2:4, /* (unused) */ + th_off:4; /* data offset */ +#endif + uint8_t th_flags; +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ +} PACKED__; +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +#include "tcp_var.h" + +#define TCPOPT_EOL 0 +#define TCPOPT_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOLEN_MAXSEG 4 +#define TCPOPT_WINDOW 3 +#define TCPOLEN_WINDOW 3 +#define TCPOPT_SACK_PERMITTED 4 /* Experimental */ +#define TCPOLEN_SACK_PERMITTED 2 +#define TCPOPT_SACK 5 /* Experimental */ +#define TCPOPT_TIMESTAMP 8 +#define TCPOLEN_TIMESTAMP 10 +#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ + +#define TCPOPT_TSTAMP_HDR \ + (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP) + +/* + * Default maximum segment size for TCP. + * With an IP MSS of 576, this is 536, + * but 512 is probably more convenient. + * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)). + * + * We make this 1460 because we only care about Ethernet in the qemu context. + */ +#define TCP_MSS 1460 + +#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ + +#define TCP_MAX_WINSHIFT 14 /* maximum window shift */ + +/* + * User-settable options (used with setsockopt). + * + * We don't use the system headers on unix because we have conflicting + * local structures. We can't avoid the system definitions on Windows, + * so we undefine them. + */ +#undef TCP_NODELAY +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#undef TCP_MAXSEG +/* #define TCP_MAXSEG 0x02 */ /* set maximum segment size */ + +/* + * TCP FSM state definitions. + * Per RFC793, September, 1981. + */ + +#define TCP_NSTATES 11 + +#define TCPS_CLOSED 0 /* closed */ +#define TCPS_LISTEN 1 /* listening for connection */ +#define TCPS_SYN_SENT 2 /* active, have sent syn */ +#define TCPS_SYN_RECEIVED 3 /* have send and received syn */ +/* states < TCPS_ESTABLISHED are those where connections not established */ +#define TCPS_ESTABLISHED 4 /* established */ +#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +/* states > TCPS_CLOSE_WAIT are those where user has closed */ +#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ +/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ +#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ + +#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED) +#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED) +#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT) + +/* + * TCP sequence numbers are 32 bit integers operated + * on with modular arithmetic. These macros can be + * used to compare such integers. + */ +#define SEQ_LT(a,b) ((int)((a)-(b)) < 0) +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define SEQ_GT(a,b) ((int)((a)-(b)) > 0) +#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0) + +/* + * Macros to initialize tcp sequence numbers for + * send and receive from initial send and receive + * sequence numbers. + */ +#define tcp_rcvseqinit(tp) \ + (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1 + +#define tcp_sendseqinit(tp) \ + (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss + +#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */ + +extern tcp_seq tcp_iss; /* tcp initial send seq # */ + +extern char *tcpstates[]; + + +extern int tcp_reass(register struct tcpcb *, register struct tcpiphdr *, + struct SLIRPmbuf *); +extern void tcp_input(register struct SLIRPmbuf *, int, + struct SLIRPsocket *); +extern void tcp_dooptions(struct tcpcb *, uint8_t *, int, + struct tcpiphdr *); +extern void tcp_xmit_timer(register struct tcpcb *, int); +extern int tcp_mss(register struct tcpcb *, u_int); + +extern int tcp_output(register struct tcpcb *); +extern void tcp_setpersist(register struct tcpcb *); + +extern void tcp_init(void); +extern void tcp_template(struct tcpcb *); +extern void tcp_respond(struct tcpcb *, register struct tcpiphdr *, + register struct SLIRPmbuf *,tcp_seq,tcp_seq,int); +extern struct tcpcb *tcp_newtcpcb(struct SLIRPsocket *); +extern struct tcpcb *tcp_close(register struct tcpcb *); +extern void tcp_drain(void); +extern void tcp_sockclosed(struct tcpcb *); +extern int tcp_fconnect(struct SLIRPsocket *); +extern void tcp_connect(struct SLIRPsocket *); +extern int tcp_attach(struct SLIRPsocket *); +extern uint8_t tcp_tos(struct SLIRPsocket *); +extern int tcp_emu(struct SLIRPsocket *, struct SLIRPmbuf *); +extern int tcp_ctl(struct SLIRPsocket *); +extern struct tcpcb *tcp_drop(struct tcpcb *tp, int err); + + +#endif diff --git a/src/tcp_input.c b/src/tcp_input.c new file mode 100644 index 0000000..e80b278 --- /dev/null +++ b/src/tcp_input.c @@ -0,0 +1,1735 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94 + * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp + */ +#ifdef _WIN32 +# include +# undef errno +# define errno (WSAGetLastError()) +#else +# include +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" +#include "ip_icmp.h" +#include "tcp.h" + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +struct SLIRPsocket tcb; + +int tcprexmtthresh = 3; +struct SLIRPsocket *tcp_last_so = &tcb; + +tcp_seq tcp_iss; /* tcp initial send seq # */ + +#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ) + +/* for modulo comparisons of timestamps */ +#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0) +#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0) + +/* + * Insert segment ti into reassembly queue of tcp with + * control block tp. Return TH_FIN if reassembly now includes + * a segment with FIN. The macro form does the common case inline + * (segment is the next to be received on an established connection, + * and the queue is empty), avoiding linkage into and removal + * from the queue and repetition of various conversions. + * Set DELACK for segments received in order, but ack immediately + * when segments are out of order (so fast retransmit can work). + */ +#ifndef USE_REDIR +# define tcp_emu(so, m) (1) +#endif +#ifdef TCP_ACK_HACK +#define TCP_REASS(tp, ti, m, so, flags) {\ + if ((ti)->ti_seq == (tp)->rcv_nxt && \ + (tp)->seg_next == (tcpiphdrp_32)(tp) && \ + (tp)->t_state == TCPS_ESTABLISHED) {\ + if (ti->ti_flags & TH_PUSH) \ + tp->t_flags |= TF_ACKNOW; \ + else \ + tp->t_flags |= TF_DELACK; \ + (tp)->rcv_nxt += (ti)->ti_len; \ + flags = (ti)->ti_flags & TH_FIN; \ + tcpstat.tcps_rcvpack++;\ + tcpstat.tcps_rcvbyte += (ti)->ti_len;\ + if (so->so_emu) { \ + if (tcp_emu((so),(m))) sbappend((so), (m)); \ + } else \ + sbappend((so), (m)); \ +/* sorwakeup(so); */ \ + } else {\ + (flags) = tcp_reass((tp), (ti), (m)); \ + tp->t_flags |= TF_ACKNOW; \ + } \ +} +#else +#define TCP_REASS(tp, ti, m, so, flags) { \ + if ((ti)->ti_seq == (tp)->rcv_nxt && \ + (tp)->seg_next == (tcpiphdrp_32)(tp) && \ + (tp)->t_state == TCPS_ESTABLISHED) { \ + tp->t_flags |= TF_DELACK; \ + (tp)->rcv_nxt += (ti)->ti_len; \ + flags = (ti)->ti_flags & TH_FIN; \ + tcpstat.tcps_rcvpack++;\ + tcpstat.tcps_rcvbyte += (ti)->ti_len;\ + if (so->so_emu) { \ + if (tcp_emu((so),(m))) sbappend(so, (m)); \ + } else \ + sbappend((so), (m)); \ +/* sorwakeup(so); */ \ + } else { \ + (flags) = tcp_reass((tp), (ti), (m)); \ + tp->t_flags |= TF_ACKNOW; \ + } \ +} +#endif + +int +tcp_reass(struct tcpcb *tp, struct tcpiphdr *ti, struct SLIRPmbuf *m) +{ + struct tcpiphdr *q; + struct SLIRPsocket *so = tp->t_socket; + int flags; + + /* + * Call with ti==0 after become established to + * force pre-ESTABLISHED data up to user socket. + */ + if (ti == 0) + goto present; + + /* + * Find a segment which begins after this one does. + */ + for (q = (struct tcpiphdr *)tp->seg_next; q != (struct tcpiphdr *)tp; + q = (struct tcpiphdr *)q->ti_next) + if (SEQ_GT(q->ti_seq, ti->ti_seq)) + break; + + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) { + int i; + q = (struct tcpiphdr *)q->ti_prev; + /* conversion to int (in i) handles seq wraparound */ + i = q->ti_seq + q->ti_len - ti->ti_seq; + if (i > 0) { + if (i >= ti->ti_len) { + tcpstat.tcps_rcvduppack++; + tcpstat.tcps_rcvdupbyte += ti->ti_len; + m_freem(m); + /* + * Try to present any queued data + * at the left window edge to the user. + * This is needed after the 3-WHS + * completes. + */ + goto present; /* ??? */ + } + m_adj(m, i); + ti->ti_len -= i; + ti->ti_seq += i; + } + q = (struct tcpiphdr *)(q->ti_next); + } + tcpstat.tcps_rcvoopack++; + tcpstat.tcps_rcvoobyte += ti->ti_len; + REASS_MBUF(ti) = (mbufp_32) m; /* XXX */ + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (q != (struct tcpiphdr *)tp) { + int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; + if (i <= 0) + break; + if (i < q->ti_len) { + q->ti_seq += i; + q->ti_len -= i; + m_adj((struct SLIRPmbuf *) REASS_MBUF(q), i); + break; + } + q = (struct tcpiphdr *)q->ti_next; + m = (struct SLIRPmbuf *) REASS_MBUF((struct tcpiphdr *)q->ti_prev); + remque_32((void *)(q->ti_prev)); + m_freem(m); + } + + /* + * Stick new segment in its place. + */ + insque_32(ti, (void *)(q->ti_prev)); + +present: + /* + * Present data to user, advancing rcv_nxt through + * completed sequence space. + */ + if (!TCPS_HAVEESTABLISHED(tp->t_state)) + return (0); + ti = (struct tcpiphdr *) tp->seg_next; + if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt) + return (0); + if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len) + return (0); + do { + tp->rcv_nxt += ti->ti_len; + flags = ti->ti_flags & TH_FIN; + remque_32(ti); + m = (struct SLIRPmbuf *) REASS_MBUF(ti); /* XXX */ + ti = (struct tcpiphdr *)ti->ti_next; +/* if (so->so_state & SS_FCANTRCVMORE) */ + if (so->so_state & SS_FCANTSENDMORE) + m_freem(m); + else { +#ifdef USE_REDIR + if (so->so_emu) { + if (tcp_emu(so,m)) sbappend(so, m); + } else +#endif + sbappend(so, m); + } + } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt); +/* sorwakeup(so); */ + return (flags); +} + +/* + * TCP input routine, follows pages 65-76 of the + * protocol specification dated September, 1981 very closely. + */ +void +tcp_input(struct SLIRPmbuf *m, int iphlen, struct SLIRPsocket *inso) +{ + struct ip save_ip, *ip; + struct tcpiphdr *ti; + SLIRPcaddr_t optp = NULL; + int optlen = 0; + int len, tlen, off; + struct tcpcb *tp = 0; + int tiflags; + struct SLIRPsocket *so = 0; + int todrop, acked, ourfinisacked, needoutput = 0; +/* int dropsocket = 0; */ + int iss = 0; + u_long tiwin; + int ret; +/* int ts_present = 0; */ + + DEBUG_CALL("tcp_input"); + DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n", + (long )m, iphlen, (long )inso )); + + /* + * If called with m == 0, then we're continuing the connect + */ + if (m == NULL) { + so = inso; + + /* Re-set a few variables */ + tp = sototcpcb(so); + m = so->so_m; + so->so_m = 0; + ti = so->so_ti; + tiwin = ti->ti_win; + tiflags = ti->ti_flags; + + goto cont_conn; + } + + + tcpstat.tcps_rcvtotal++; + /* + * Get IP and TCP header together in first SLIRPmbuf. + * Note: IP leaves IP header in first SLIRPmbuf. + */ + ti = mtod(m, struct tcpiphdr *); + if (iphlen > sizeof(struct ip )) { + ip_stripoptions(m, (struct SLIRPmbuf *)0); + iphlen=sizeof(struct ip ); + } + /* XXX Check if too short */ + + + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + ip=mtod(m, struct ip *); + save_ip = *ip; + save_ip.ip_len+= iphlen; + + /* + * Checksum extended TCP header and data. + */ + tlen = ((struct ip *)ti)->ip_len; + ti->ti_next = ti->ti_prev = 0; + ti->ti_x1 = 0; + ti->ti_len = htons((uint16_t)tlen); + len = sizeof(struct ip ) + tlen; + /* keep checksum for ICMP reply + * ti->ti_sum = cksum(m, len); + * if (ti->ti_sum) { */ + if(cksum(m, len)) { + tcpstat.tcps_rcvbadsum++; + goto drop; + } + + /* + * Check that TCP offset makes sense, + * pull out TCP options and adjust length. XXX + */ + off = ti->ti_off << 2; + if (off < sizeof (struct tcphdr) || off > tlen) { + tcpstat.tcps_rcvbadoff++; + goto drop; + } + tlen -= off; + ti->ti_len = tlen; + if (off > sizeof (struct tcphdr)) { + optlen = off - sizeof (struct tcphdr); + optp = mtod(m, SLIRPcaddr_t) + sizeof (struct tcpiphdr); + + /* + * Do quick retrieval of timestamp options ("options + * prediction?"). If timestamp is the only option and it's + * formatted as recommended in RFC 1323 appendix A, we + * quickly get the values now and not bother calling + * tcp_dooptions(), etc. + */ +/* if ((optlen == TCPOLEN_TSTAMP_APPA || + * (optlen > TCPOLEN_TSTAMP_APPA && + * optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) && + * *(uint32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) && + * (ti->ti_flags & TH_SYN) == 0) { + * ts_present = 1; + * ts_val = ntohl(*(uint32_t *)(optp + 4)); + * ts_ecr = ntohl(*(uint32_t *)(optp + 8)); + * optp = NULL; / * we've parsed the options * / + * } + */ + } + tiflags = ti->ti_flags; + + /* + * Convert TCP protocol specific fields to host format. + */ + NTOHL(ti->ti_seq); + NTOHL(ti->ti_ack); + NTOHS(ti->ti_win); + NTOHS(ti->ti_urp); + + /* + * Drop TCP, IP headers and TCP options. + */ + m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + + /* + * Locate pcb for segment. + */ +findso: + so = tcp_last_so; + if (so->so_fport != ti->ti_dport || + so->so_lport != ti->ti_sport || + so->so_laddr.s_addr != ti->ti_src.s_addr || + so->so_faddr.s_addr != ti->ti_dst.s_addr) { + so = solookup(&tcb, ti->ti_src, ti->ti_sport, + ti->ti_dst, ti->ti_dport); + if (so) + tcp_last_so = so; + ++tcpstat.tcps_socachemiss; + } + + /* + * If the state is CLOSED (i.e., TCB does not exist) then + * all data in the incoming segment is discarded. + * If the TCB exists but is in CLOSED state, it is embryonic, + * but should either do a listen or a connect soon. + * + * state == CLOSED means we've done socreate() but haven't + * attached it to a protocol yet... + * + * XXX If a TCB does not exist, and the TH_SYN flag is + * the only flag set, then create a session, mark it + * as if it was LISTENING, and continue... + */ + if (so == 0) { + if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN) + goto dropwithreset; + + if ((so = socreate()) == NULL) + goto dropwithreset; + if (tcp_attach(so) < 0) { + free(so); /* Not sofree (if it failed, it's not insqued) */ + goto dropwithreset; + } + + sbreserve(&so->so_snd, tcp_sndspace); + sbreserve(&so->so_rcv, tcp_rcvspace); + + /* tcp_last_so = so; */ /* XXX ? */ + /* tp = sototcpcb(so); */ + + so->so_laddr = ti->ti_src; + so->so_lport = ti->ti_sport; + so->so_faddr = ti->ti_dst; + so->so_fport = ti->ti_dport; + + if ((so->so_iptos = tcp_tos(so)) == 0) + so->so_iptos = ((struct ip *)ti)->ip_tos; + + tp = sototcpcb(so); + tp->t_state = TCPS_LISTEN; + } + + /* + * If this is a still-connecting socket, this probably + * a retransmit of the SYN. Whether it's a retransmit SYN + * or something else, we nuke it. + */ + if (so->so_state & SS_ISFCONNECTING) + goto drop; + + tp = sototcpcb(so); + + /* XXX Should never fail */ + if (tp == 0) + goto dropwithreset; + if (tp->t_state == TCPS_CLOSED) + goto drop; + + /* Unscale the window into a 32-bit value. */ +/* if ((tiflags & TH_SYN) == 0) + * tiwin = ti->ti_win << tp->snd_scale; + * else + */ + tiwin = ti->ti_win; + + /* + * Segment received on connection. + * Reset idle time and keep-alive timer. + */ + tp->t_idle = 0; + if (so_options) + tp->t_timer[TCPT_KEEP] = tcp_keepintvl; + else + tp->t_timer[TCPT_KEEP] = tcp_keepidle; + + /* + * Process options if not in LISTEN state, + * else do it below (after getting remote address). + */ + if (optp && tp->t_state != TCPS_LISTEN) + tcp_dooptions(tp, (u_char *)optp, optlen, ti); +/* , */ +/* &ts_present, &ts_val, &ts_ecr); */ + + /* + * Header prediction: check for the two common cases + * of a uni-directional data xfer. If the packet has + * no control flags, is in-sequence, the window didn't + * change and we're not retransmitting, it's a + * candidate. If the length is zero and the ack moved + * forward, we're the sender side of the xfer. Just + * free the data acked & wake any higher level process + * that was blocked waiting for space. If the length + * is non-zero and the ack didn't move, we're the + * receiver side. If we're getting packets in-order + * (the reassembly queue is empty), add the data to + * the socket buffer and note that we need a delayed ack. + * + * XXX Some of these tests are not needed + * eg: the tiwin == tp->snd_wnd prevents many more + * predictions.. with no *real* advantage.. + */ + if (tp->t_state == TCPS_ESTABLISHED && + (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && +/* (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) && */ + ti->ti_seq == tp->rcv_nxt && + tiwin && tiwin == tp->snd_wnd && + tp->snd_nxt == tp->snd_max) { + /* + * If last ACK falls within this segment's sequence numbers, + * record the timestamp. + */ +/* if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) && + * SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) { + * tp->ts_recent_age = tcp_now; + * tp->ts_recent = ts_val; + * } + */ + if (ti->ti_len == 0) { + if (SEQ_GT(ti->ti_ack, tp->snd_una) && + SEQ_LEQ(ti->ti_ack, tp->snd_max) && + tp->snd_cwnd >= tp->snd_wnd) { + /* + * this is a pure ack for outstanding data. + */ + ++tcpstat.tcps_predack; +/* if (ts_present) + * tcp_xmit_timer(tp, tcp_now-ts_ecr+1); + * else + */ if (tp->t_rtt && + SEQ_GT(ti->ti_ack, tp->t_rtseq)) + tcp_xmit_timer(tp, tp->t_rtt); + acked = ti->ti_ack - tp->snd_una; + tcpstat.tcps_rcvackpack++; + tcpstat.tcps_rcvackbyte += acked; + sbdrop(&so->so_snd, acked); + tp->snd_una = ti->ti_ack; + m_freem(m); + + /* + * If all outstanding data are acked, stop + * retransmit timer, otherwise restart timer + * using current (possibly backed-off) value. + * If process is waiting for space, + * wakeup/selwakeup/signal. If data + * are ready to send, let tcp_output + * decide between more output or persist. + */ + if (tp->snd_una == tp->snd_max) + tp->t_timer[TCPT_REXMT] = 0; + else if (tp->t_timer[TCPT_PERSIST] == 0) + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + + /* + * There's room in so_snd, sowwakup will read() + * from the socket if we can + */ +/* if (so->so_snd.sb_flags & SB_NOTIFY) + * sowwakeup(so); + */ + /* + * This is called because sowwakeup might have + * put data into so_snd. Since we don't so sowwakeup, + * we don't need this.. XXX??? + */ + if (so->so_snd.sb_cc) + (void) tcp_output(tp); + + return; + } + } else if (ti->ti_ack == tp->snd_una && + tp->seg_next == (tcpiphdrp_32)tp && + ti->ti_len <= sbspace(&so->so_rcv)) { + /* + * this is a pure, in-sequence data packet + * with nothing on the reassembly queue and + * we have enough buffer space to take it. + */ + ++tcpstat.tcps_preddat; + tp->rcv_nxt += ti->ti_len; + tcpstat.tcps_rcvpack++; + tcpstat.tcps_rcvbyte += ti->ti_len; + +#ifdef USE_REDIR + /* + * Add data to socket buffer. + */ + if (so->so_emu) { + if (tcp_emu(so,m)) sbappend(so, m); + } else +#endif + sbappend(so, m); + + /* + * XXX This is called when data arrives. Later, check + * if we can actually write() to the socket + * XXX Need to check? It's be NON_BLOCKING + */ +/* sorwakeup(so); */ + + /* + * If this is a short packet, then ACK now - with Nagel + * congestion avoidance sender won't send more until + * he gets an ACK. + * + * It is better to not delay acks at all to maximize + * TCP throughput. See RFC 2581. + */ + tp->t_flags |= TF_ACKNOW; + tcp_output(tp); + return; + } + } /* header prediction */ + /* + * Calculate amount of space in receive window, + * and then do TCP input processing. + * Receive window is amount of space in rcv queue, + * but not less than advertised window. + */ + { int win; + win = sbspace(&so->so_rcv); + if (win < 0) + win = 0; + tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt)); + } + + switch (tp->t_state) { + + /* + * If the state is LISTEN then ignore segment if it contains an RST. + * If the segment contains an ACK then it is bad and send a RST. + * If it does not contain a SYN then it is not interesting; drop it. + * Don't bother responding if the destination was a broadcast. + * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial + * tp->iss, and send a segment: + * + * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. + * Fill in remote peer address fields if not previously specified. + * Enter SYN_RECEIVED state, and process any other fields of this + * segment in this state. + */ + case TCPS_LISTEN: { + + if (tiflags & TH_RST) + goto drop; + if (tiflags & TH_ACK) + goto dropwithreset; + if ((tiflags & TH_SYN) == 0) + goto drop; + + /* + * This has way too many gotos... + * But a bit of spaghetti code never hurt anybody :) + */ + + /* + * If this is destined for the control address, then flag to + * tcp_ctl once connected, otherwise connect + */ + if ((so->so_faddr.s_addr&htonl(0xffffff00)) == special_addr.s_addr) { + int lastbyte=ntohl(so->so_faddr.s_addr) & 0xff; + if (lastbyte != CTL_ROUTER && lastbyte != CTL_SERVER) { +#ifdef CTL_CMD + if (lastbyte==CTL_CMD || lastbyte==CTL_EXEC) { + /* Command or exec adress */ + so->so_state |= SS_CTL; + } else +#endif +#ifdef USE_REDIR + { + /* May be an add exec */ + struct ex_list *ex_ptr; + for(ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if(ex_ptr->ex_fport == so->so_fport && + lastbyte == ex_ptr->ex_addr) { + so->so_state |= SS_CTL; + break; + } + } + } +#endif + if(so->so_state & SS_CTL) goto cont_input; + } + /* CTL_ROUTER: Do nothing, tcp_fconnect will be called on it */ + } + + if (so->so_emu & EMU_NOCONNECT) { + so->so_emu &= ~EMU_NOCONNECT; + goto cont_input; + } + + if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { + u_char code=ICMP_UNREACH_NET; + DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n", + errno,strerror(errno))); + if(errno == ECONNREFUSED) { + /* ACK the SYN, send RST to refuse the connection */ + tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0, + TH_RST|TH_ACK); + } else { + if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; + HTONL(ti->ti_seq); /* restore tcp header */ + HTONL(ti->ti_ack); + HTONS(ti->ti_win); + HTONS(ti->ti_urp); + m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + *ip=save_ip; + icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno)); + } + tp = tcp_close(tp); + m_free(m); + } else { + /* + * Haven't connected yet, save the current SLIRPmbuf + * and ti, and return + * XXX Some OS's don't tell us whether the connect() + * succeeded or not. So we must time it out. + */ + so->so_m = m; + so->so_ti = ti; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->t_state = TCPS_SYN_RECEIVED; + } + return; + + cont_conn: + /* m==NULL + * Check if the connect succeeded + */ + if (so->so_state & SS_NOFDREF) { + tp = tcp_close(tp); + goto dropwithreset; + } + cont_input: + tcp_template(tp); + + if (optp) + tcp_dooptions(tp, (u_char *)optp, optlen, ti); + /* , */ + /* &ts_present, &ts_val, &ts_ecr); */ + + if (iss) + tp->iss = iss; + else + tp->iss = tcp_iss; + tcp_iss += TCP_ISSINCR/2; + tp->irs = ti->ti_seq; + tcp_sendseqinit(tp); + tcp_rcvseqinit(tp); + tp->t_flags |= TF_ACKNOW; + tp->t_state = TCPS_SYN_RECEIVED; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tcpstat.tcps_accepts++; + goto trimthenstep6; + } /* case TCPS_LISTEN */ + + /* + * If the state is SYN_SENT: + * if seg contains an ACK, but not for our SYN, drop the input. + * if seg contains a RST, then drop the connection. + * if seg does not contain SYN, then drop it. + * Otherwise this is an acceptable SYN segment + * initialize tp->rcv_nxt and tp->irs + * if seg contains ack then advance tp->snd_una + * if SYN has been acked change to ESTABLISHED else SYN_RCVD state + * arrange for segment to be acked (eventually) + * continue processing rest of data/controls, beginning with URG + */ + case TCPS_SYN_SENT: + if ((tiflags & TH_ACK) && + (SEQ_LEQ(ti->ti_ack, tp->iss) || + SEQ_GT(ti->ti_ack, tp->snd_max))) + goto dropwithreset; + + if (tiflags & TH_RST) { + if (tiflags & TH_ACK) + tp = tcp_drop(tp,0); /* XXX Check t_softerror! */ + goto drop; + } + + if ((tiflags & TH_SYN) == 0) + goto drop; + if (tiflags & TH_ACK) { + tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + } + + tp->t_timer[TCPT_REXMT] = 0; + tp->irs = ti->ti_seq; + tcp_rcvseqinit(tp); + tp->t_flags |= TF_ACKNOW; + if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) { + tcpstat.tcps_connects++; + soisfconnected(so); + tp->t_state = TCPS_ESTABLISHED; + + /* Do window scaling on this connection? */ +/* if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + * (TF_RCVD_SCALE|TF_REQ_SCALE)) { + * tp->snd_scale = tp->requested_s_scale; + * tp->rcv_scale = tp->request_r_scale; + * } + */ + (void) tcp_reass(tp, (struct tcpiphdr *)0, + (struct SLIRPmbuf *)0); + /* + * if we didn't have to retransmit the SYN, + * use its rtt as our initial srtt & rtt var. + */ + if (tp->t_rtt) + tcp_xmit_timer(tp, tp->t_rtt); + } else + tp->t_state = TCPS_SYN_RECEIVED; + +trimthenstep6: + /* + * Advance ti->ti_seq to correspond to first data byte. + * If data, trim to stay within window, + * dropping FIN if necessary. + */ + ti->ti_seq++; + if (ti->ti_len > tp->rcv_wnd) { + todrop = ti->ti_len - tp->rcv_wnd; + m_adj(m, -todrop); + ti->ti_len = tp->rcv_wnd; + tiflags &= ~TH_FIN; + tcpstat.tcps_rcvpackafterwin++; + tcpstat.tcps_rcvbyteafterwin += todrop; + } + tp->snd_wl1 = ti->ti_seq - 1; + tp->rcv_up = ti->ti_seq; + goto step6; + } /* switch tp->t_state */ + /* + * States other than LISTEN or SYN_SENT. + * First check timestamp, if present. + * Then check that at least some bytes of segment are within + * receive window. If segment begins before rcv_nxt, + * drop leading data (and SYN); if nothing left, just ack. + * + * RFC 1323 PAWS: If we have a timestamp reply on this segment + * and it's less than ts_recent, drop it. + */ +/* if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent && + * TSTMP_LT(ts_val, tp->ts_recent)) { + * + */ /* Check to see if ts_recent is over 24 days old. */ +/* if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) { + */ /* + * * Invalidate ts_recent. If this segment updates + * * ts_recent, the age will be reset later and ts_recent + * * will get a valid value. If it does not, setting + * * ts_recent to zero will at least satisfy the + * * requirement that zero be placed in the timestamp + * * echo reply when ts_recent isn't valid. The + * * age isn't reset until we get a valid ts_recent + * * because we don't want out-of-order segments to be + * * dropped when ts_recent is old. + * */ +/* tp->ts_recent = 0; + * } else { + * tcpstat.tcps_rcvduppack++; + * tcpstat.tcps_rcvdupbyte += ti->ti_len; + * tcpstat.tcps_pawsdrop++; + * goto dropafterack; + * } + * } + */ + + todrop = tp->rcv_nxt - ti->ti_seq; + if (todrop > 0) { + if (tiflags & TH_SYN) { + tiflags &= ~TH_SYN; + ti->ti_seq++; + if (ti->ti_urp > 1) + ti->ti_urp--; + else + tiflags &= ~TH_URG; + todrop--; + } + /* + * Following if statement from Stevens, vol. 2, p. 960. + */ + if (todrop > ti->ti_len + || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) { + /* + * Any valid FIN must be to the left of the window. + * At this point the FIN must be a duplicate or out + * of sequence; drop it. + */ + tiflags &= ~TH_FIN; + + /* + * Send an ACK to resynchronize and drop any data. + * But keep on processing for RST or ACK. + */ + tp->t_flags |= TF_ACKNOW; + todrop = ti->ti_len; + tcpstat.tcps_rcvduppack++; + tcpstat.tcps_rcvdupbyte += todrop; + } else { + tcpstat.tcps_rcvpartduppack++; + tcpstat.tcps_rcvpartdupbyte += todrop; + } + m_adj(m, todrop); + ti->ti_seq += todrop; + ti->ti_len -= todrop; + if (ti->ti_urp > todrop) + ti->ti_urp -= todrop; + else { + tiflags &= ~TH_URG; + ti->ti_urp = 0; + } + } + /* + * If new data are received on a connection after the + * user processes are gone, then RST the other end. + */ + if ((so->so_state & SS_NOFDREF) && + tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) { + tp = tcp_close(tp); + tcpstat.tcps_rcvafterclose++; + goto dropwithreset; + } + + /* + * If segment ends after window, drop trailing data + * (and PUSH and FIN); if nothing left, just ACK. + */ + todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); + if (todrop > 0) { + tcpstat.tcps_rcvpackafterwin++; + if (todrop >= ti->ti_len) { + tcpstat.tcps_rcvbyteafterwin += ti->ti_len; + /* + * If a new connection request is received + * while in TIME_WAIT, drop the old connection + * and start over if the sequence numbers + * are above the previous ones. + */ + if (tiflags & TH_SYN && + tp->t_state == TCPS_TIME_WAIT && + SEQ_GT(ti->ti_seq, tp->rcv_nxt)) { + iss = tp->rcv_nxt + TCP_ISSINCR; + tp = tcp_close(tp); + goto findso; + } + /* + * If window is closed can only take segments at + * window edge, and have to drop data and PUSH from + * incoming segments. Continue processing, but + * remember to ack. Otherwise, drop segment + * and ack. + */ + if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) { + tp->t_flags |= TF_ACKNOW; + tcpstat.tcps_rcvwinprobe++; + } else + goto dropafterack; + } else + tcpstat.tcps_rcvbyteafterwin += todrop; + m_adj(m, -todrop); + ti->ti_len -= todrop; + tiflags &= ~(TH_PUSH|TH_FIN); + } + + /* + * If last ACK falls within this segment's sequence numbers, + * record its timestamp. + */ +/* if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) && + * SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len + + * ((tiflags & (TH_SYN|TH_FIN)) != 0))) { + * tp->ts_recent_age = tcp_now; + * tp->ts_recent = ts_val; + * } + */ + + /* + * If the RST bit is set examine the state: + * SYN_RECEIVED STATE: + * If passive open, return to LISTEN state. + * If active open, inform user that connection was refused. + * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: + * Inform user that connection was reset, and close tcb. + * CLOSING, LAST_ACK, TIME_WAIT STATES + * Close the tcb. + */ + if (tiflags&TH_RST) switch (tp->t_state) { + + case TCPS_SYN_RECEIVED: +/* so->so_error = ECONNREFUSED; */ + goto close; + + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: +/* so->so_error = ECONNRESET; */ + close: + tp->t_state = TCPS_CLOSED; + tcpstat.tcps_drops++; + tp = tcp_close(tp); + goto drop; + + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: + tp = tcp_close(tp); + goto drop; + } + + /* + * If a SYN is in the window, then this is an + * error and we send an RST and drop the connection. + */ + if (tiflags & TH_SYN) { + tp = tcp_drop(tp,0); + goto dropwithreset; + } + + /* + * If the ACK bit is off we drop the segment and return. + */ + if ((tiflags & TH_ACK) == 0) goto drop; + + /* + * Ack processing. + */ + switch (tp->t_state) { + /* + * In SYN_RECEIVED state if the ack ACKs our SYN then enter + * ESTABLISHED state and continue processing, otherwise + * send an RST. una<=ack<=max + */ + case TCPS_SYN_RECEIVED: + + if (SEQ_GT(tp->snd_una, ti->ti_ack) || + SEQ_GT(ti->ti_ack, tp->snd_max)) + goto dropwithreset; + tcpstat.tcps_connects++; + tp->t_state = TCPS_ESTABLISHED; + /* + * The sent SYN is ack'ed with our sequence number +1 + * The first data byte already in the buffer will get + * lost if no correction is made. This is only needed for + * SS_CTL since the buffer is empty otherwise. + * tp->snd_una++; or: + */ + tp->snd_una=ti->ti_ack; + if (so->so_state & SS_CTL) { + /* So tcp_ctl reports the right state */ + ret = tcp_ctl(so); + if (ret == 1) { + soisfconnected(so); + so->so_state &= ~SS_CTL; /* success XXX */ + } else if (ret == 2) { + so->so_state = SS_NOFDREF; /* CTL_CMD */ + } else { + needoutput = 1; + tp->t_state = TCPS_FIN_WAIT_1; + } + } else { + soisfconnected(so); + } + + /* Do window scaling? */ +/* if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + * (TF_RCVD_SCALE|TF_REQ_SCALE)) { + * tp->snd_scale = tp->requested_s_scale; + * tp->rcv_scale = tp->request_r_scale; + * } + */ + (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct SLIRPmbuf *)0); + tp->snd_wl1 = ti->ti_seq - 1; + /* Avoid ack processing; snd_una==ti_ack => dup ack */ + goto synrx_to_est; + /* fall into ... */ + + /* + * In ESTABLISHED state: drop duplicate ACKs; ACK out of range + * ACKs. If the ack is in the range + * tp->snd_una < ti->ti_ack <= tp->snd_max + * then advance tp->snd_una to ti->ti_ack and drop + * data from the retransmission queue. If this ACK reflects + * more up to date window information we update our window information. + */ + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: + + if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { + if (ti->ti_len == 0 && tiwin == tp->snd_wnd) { + tcpstat.tcps_rcvdupack++; + DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n", + (long )m, (long )so)); + /* + * If we have outstanding data (other than + * a window probe), this is a completely + * duplicate ack (ie, window info didn't + * change), the ack is the biggest we've + * seen and we've seen exactly our rexmt + * threshold of them, assume a packet + * has been dropped and retransmit it. + * Kludge snd_nxt & the congestion + * window so we send only this one + * packet. + * + * We know we're losing at the current + * window size so do congestion avoidance + * (set ssthresh to half the current window + * and pull our congestion window back to + * the new ssthresh). + * + * Dup acks mean that packets have left the + * network (they're now cached at the receiver) + * so bump cwnd by the amount in the receiver + * to keep a constant cwnd packets in the + * network. + */ + if (tp->t_timer[TCPT_REXMT] == 0 || + ti->ti_ack != tp->snd_una) + tp->t_dupacks = 0; + else if (++tp->t_dupacks == tcprexmtthresh) { + tcp_seq onxt = tp->snd_nxt; + u_int win = + min(tp->snd_wnd, tp->snd_cwnd) / 2 / + tp->t_maxseg; + + if (win < 2) + win = 2; + tp->snd_ssthresh = win * tp->t_maxseg; + tp->t_timer[TCPT_REXMT] = 0; + tp->t_rtt = 0; + tp->snd_nxt = ti->ti_ack; + tp->snd_cwnd = tp->t_maxseg; + (void) tcp_output(tp); + tp->snd_cwnd = tp->snd_ssthresh + + tp->t_maxseg * tp->t_dupacks; + if (SEQ_GT(onxt, tp->snd_nxt)) + tp->snd_nxt = onxt; + goto drop; + } else if (tp->t_dupacks > tcprexmtthresh) { + tp->snd_cwnd += tp->t_maxseg; + (void) tcp_output(tp); + goto drop; + } + } else + tp->t_dupacks = 0; + break; + } + synrx_to_est: + /* + * If the congestion window was inflated to account + * for the other side's cached packets, retract it. + */ + if (tp->t_dupacks > tcprexmtthresh && + tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; + tp->t_dupacks = 0; + if (SEQ_GT(ti->ti_ack, tp->snd_max)) { + tcpstat.tcps_rcvacktoomuch++; + goto dropafterack; + } + acked = ti->ti_ack - tp->snd_una; + tcpstat.tcps_rcvackpack++; + tcpstat.tcps_rcvackbyte += acked; + + /* + * If we have a timestamp reply, update smoothed + * round trip time. If no timestamp is present but + * transmit timer is running and timed sequence + * number was acked, update smoothed round trip time. + * Since we now have an rtt measurement, cancel the + * timer backoff (cf., Phil Karn's retransmit alg.). + * Recompute the initial retransmit timer. + */ +/* if (ts_present) + * tcp_xmit_timer(tp, tcp_now-ts_ecr+1); + * else + */ + if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) + tcp_xmit_timer(tp,tp->t_rtt); + + /* + * If all outstanding data is acked, stop retransmit + * timer and remember to restart (more output or persist). + * If there is more data to be acked, restart retransmit + * timer, using current (possibly backed-off) value. + */ + if (ti->ti_ack == tp->snd_max) { + tp->t_timer[TCPT_REXMT] = 0; + needoutput = 1; + } else if (tp->t_timer[TCPT_PERSIST] == 0) + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + /* + * When new data is acked, open the congestion window. + * If the window gives us less than ssthresh packets + * in flight, open exponentially (maxseg per packet). + * Otherwise open linearly: maxseg per window + * (maxseg^2 / cwnd per packet). + */ + { + u_int cw = tp->snd_cwnd; + u_int incr = tp->t_maxseg; + + if (cw > tp->snd_ssthresh) + incr = incr * incr / cw; + tp->snd_cwnd = min(cw + incr, (u_int)(TCP_MAXWIN<snd_scale)); + } + if (acked > (int)so->so_snd.sb_cc) { + tp->snd_wnd -= so->so_snd.sb_cc; + sbdrop(&so->so_snd, (int )so->so_snd.sb_cc); + ourfinisacked = 1; + } else { + sbdrop(&so->so_snd, acked); + tp->snd_wnd -= acked; + ourfinisacked = 0; + } + /* + * XXX sowwakup is called when data is acked and there's room for + * for more data... it should read() the socket + */ +/* if (so->so_snd.sb_flags & SB_NOTIFY) + * sowwakeup(so); + */ + tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + + switch (tp->t_state) { + + /* + * In FIN_WAIT_1 STATE in addition to the processing + * for the ESTABLISHED state if our FIN is now acknowledged + * then enter FIN_WAIT_2. + */ + case TCPS_FIN_WAIT_1: + if (ourfinisacked) { + /* + * If we can't receive any more + * data, then closing user can proceed. + * Starting the timer is contrary to the + * specification, but if we don't get a FIN + * we'll hang forever. + */ + if (so->so_state & SS_FCANTRCVMORE) { + soisfdisconnected(so); + tp->t_timer[TCPT_2MSL] = tcp_maxidle; + } + tp->t_state = TCPS_FIN_WAIT_2; + } + break; + + /* + * In CLOSING STATE in addition to the processing for + * the ESTABLISHED state if the ACK acknowledges our FIN + * then enter the TIME-WAIT state, otherwise ignore + * the segment. + */ + case TCPS_CLOSING: + if (ourfinisacked) { + tp->t_state = TCPS_TIME_WAIT; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisfdisconnected(so); + } + break; + + /* + * In LAST_ACK, we may still be waiting for data to drain + * and/or to be acked, as well as for the ack of our FIN. + * If our FIN is now acknowledged, delete the TCB, + * enter the closed state and return. + */ + case TCPS_LAST_ACK: + if (ourfinisacked) { + tp = tcp_close(tp); + goto drop; + } + break; + + /* + * In TIME_WAIT state the only thing that should arrive + * is a retransmission of the remote FIN. Acknowledge + * it and restart the finack timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + goto dropafterack; + } + } /* switch(tp->t_state) */ + +step6: + /* + * Update window information. + * Don't look at window if no ACK: TAC's send garbage on first SYN. + */ + if ((tiflags & TH_ACK) && + (SEQ_LT(tp->snd_wl1, ti->ti_seq) || + (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) || + (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) { + /* keep track of pure window updates */ + if (ti->ti_len == 0 && + tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd) + tcpstat.tcps_rcvwinupd++; + tp->snd_wnd = tiwin; + tp->snd_wl1 = ti->ti_seq; + tp->snd_wl2 = ti->ti_ack; + if (tp->snd_wnd > tp->max_sndwnd) + tp->max_sndwnd = tp->snd_wnd; + needoutput = 1; + } + + /* + * Process segments with URG. + */ + if ((tiflags & TH_URG) && ti->ti_urp && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* + * This is a kludge, but if we receive and accept + * random urgent pointers, we'll crash in + * soreceive. It's hard to imagine someone + * actually wanting to send this much urgent data. + */ + if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) { + ti->ti_urp = 0; + tiflags &= ~TH_URG; + goto dodata; + } + /* + * If this segment advances the known urgent pointer, + * then mark the data stream. This should not happen + * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since + * a FIN has been received from the remote side. + * In these states we ignore the URG. + * + * According to RFC961 (Assigned Protocols), + * the urgent pointer points to the last octet + * of urgent data. We continue, however, + * to consider it to indicate the first octet + * of data past the urgent section as the original + * spec states (in one of two places). + */ + if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) { + tp->rcv_up = ti->ti_seq + ti->ti_urp; + so->so_urgc = so->so_rcv.sb_cc + + (tp->rcv_up - tp->rcv_nxt); /* -1; */ + tp->rcv_up = ti->ti_seq + ti->ti_urp; + + } + } else + /* + * If no out of band data is expected, + * pull receive urgent pointer along + * with the receive window. + */ + if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) + tp->rcv_up = tp->rcv_nxt; +dodata: + + /* + * Process the segment text, merging it into the TCP sequencing queue, + * and arranging for acknowledgment of receipt if necessary. + * This process logically involves adjusting tp->rcv_wnd as data + * is presented to the user (this happens in tcp_usrreq.c, + * case PRU_RCVD). If a FIN has already been received on this + * connection then we just ignore the text. + */ + if ((ti->ti_len || (tiflags&TH_FIN)) && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + TCP_REASS(tp, ti, m, so, tiflags); + /* + * Note the amount of data that peer has sent into + * our window, in order to estimate the sender's + * buffer size. + */ + len = so->so_rcv.sb_datalen - (tp->rcv_adv - tp->rcv_nxt); + } else { + m_free(m); + tiflags &= ~TH_FIN; + } + + /* + * If FIN is received ACK the FIN and let the user know + * that the connection is closing. + */ + if (tiflags & TH_FIN) { + if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* + * If we receive a FIN we can't send more data, + * set it SS_FDRAIN + * Shutdown the socket if there is no rx data in the + * buffer. + * soread() is called on completion of shutdown() and + * will got to TCPS_LAST_ACK, and use tcp_output() + * to send the FIN. + */ +/* sofcantrcvmore(so); */ + sofwdrain(so); + + tp->t_flags |= TF_ACKNOW; + tp->rcv_nxt++; + } + switch (tp->t_state) { + + /* + * In SYN_RECEIVED and ESTABLISHED STATES + * enter the CLOSE_WAIT state. + */ + case TCPS_SYN_RECEIVED: + case TCPS_ESTABLISHED: + if(so->so_emu == EMU_CTL) /* no shutdown on socket */ + tp->t_state = TCPS_LAST_ACK; + else + tp->t_state = TCPS_CLOSE_WAIT; + break; + + /* + * If still in FIN_WAIT_1 STATE FIN has not been acked so + * enter the CLOSING state. + */ + case TCPS_FIN_WAIT_1: + tp->t_state = TCPS_CLOSING; + break; + + /* + * In FIN_WAIT_2 state enter the TIME_WAIT state, + * starting the time-wait timer, turning off the other + * standard timers. + */ + case TCPS_FIN_WAIT_2: + tp->t_state = TCPS_TIME_WAIT; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisfdisconnected(so); + break; + + /* + * In TIME_WAIT state restart the 2 MSL time_wait timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + break; + } + } + + /* + * If this is a small packet, then ACK now - with Nagel + * congestion avoidance sender won't send more until + * he gets an ACK. + * + * See above. + */ +/* if (ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg) { + */ +/* if ((ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg && + * (so->so_iptos & IPTOS_LOWDELAY) == 0) || + * ((so->so_iptos & IPTOS_LOWDELAY) && + * ((struct tcpiphdr_2 *)ti)->first_char == (char)27)) { + */ + if (ti->ti_len && (unsigned)ti->ti_len <= 5 && + ((struct tcpiphdr_2 *)ti)->first_char == (char)27) { + tp->t_flags |= TF_ACKNOW; + } + + /* + * Return any desired output. + */ + if (needoutput || (tp->t_flags & TF_ACKNOW)) { + (void) tcp_output(tp); + } + return; + +dropafterack: + /* + * Generate an ACK dropping incoming segment if it occupies + * sequence space, where the ACK reflects our state. + */ + if (tiflags & TH_RST) + goto drop; + m_freem(m); + tp->t_flags |= TF_ACKNOW; + (void) tcp_output(tp); + return; + +dropwithreset: + /* reuses m if m!=NULL, m_free() unnecessary */ + if (tiflags & TH_ACK) + tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST); + else { + if (tiflags & TH_SYN) ti->ti_len++; + tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0, + TH_RST|TH_ACK); + } + + return; + +drop: + /* + * Drop space held by incoming segment and return. + */ + m_free(m); + + return; +} + + /* , ts_present, ts_val, ts_ecr) */ +/* int *ts_present; + * uint32_t *ts_val, *ts_ecr; + */ +void +tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) +{ + uint16_t mss; + int opt, optlen; + + DEBUG_CALL("tcp_dooptions"); + DEBUG_ARGS((dfd," tp = %lx cnt=%i \n", (long )tp, cnt)); + + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + optlen = cp[1]; + if (optlen <= 0) + break; + } + switch (opt) { + + default: + continue; + + case TCPOPT_MAXSEG: + if (optlen != TCPOLEN_MAXSEG) + continue; + if (!(ti->ti_flags & TH_SYN)) + continue; + memcpy((char *) &mss, (char *) cp + 2, sizeof(mss)); + NTOHS(mss); + (void) tcp_mss(tp, mss); /* sets t_maxseg */ + break; + +/* case TCPOPT_WINDOW: + * if (optlen != TCPOLEN_WINDOW) + * continue; + * if (!(ti->ti_flags & TH_SYN)) + * continue; + * tp->t_flags |= TF_RCVD_SCALE; + * tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT); + * break; + */ +/* case TCPOPT_TIMESTAMP: + * if (optlen != TCPOLEN_TIMESTAMP) + * continue; + * *ts_present = 1; + * memcpy((char *) ts_val, (char *)cp + 2, sizeof(*ts_val)); + * NTOHL(*ts_val); + * memcpy((char *) ts_ecr, (char *)cp + 6, sizeof(*ts_ecr)); + * NTOHL(*ts_ecr); + * + */ /* + * * A timestamp received in a SYN makes + * * it ok to send timestamp requests and replies. + * */ +/* if (ti->ti_flags & TH_SYN) { + * tp->t_flags |= TF_RCVD_TSTMP; + * tp->ts_recent = *ts_val; + * tp->ts_recent_age = tcp_now; + * } + */ break; + } + } +} + + +/* + * Pull out of band byte out of a segment so + * it doesn't appear in the user's data queue. + * It is still reflected in the segment length for + * sequencing purposes. + */ + +#ifdef notdef + +void +tcp_pulloutofband(struct SLIRPsocket *so, struct tcpiphdr *ti, struct SLIRPmbuf *m) +{ + int cnt = ti->ti_urp - 1; + + while (cnt >= 0) { + if (m->m_len > cnt) { + char *cp = mtod(m, SLIRPcaddr_t) + cnt; + struct tcpcb *tp = sototcpcb(so); + + tp->t_iobc = *cp; + tp->t_oobflags |= TCPOOB_HAVEDATA; + memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1)); + m->m_len--; + return; + } + cnt -= m->m_len; + m = m->m_next; /* XXX WRONG! Fix it! */ + if (m == 0) + break; + } + panic("tcp_pulloutofband"); +} + +#endif /* notdef */ + +/* + * Collect new round-trip time estimate + * and update averages and current timeout. + */ + +void +tcp_xmit_timer(struct tcpcb *tp, int rtt) +{ + short delta; + + DEBUG_CALL("tcp_xmit_timer"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("rtt = %d", rtt); + + tcpstat.tcps_rttupdated++; + if (tp->t_srtt != 0) { + /* + * srtt is stored as fixed point with 3 bits after the + * binary point (i.e., scaled by 8). The following magic + * is equivalent to the smoothing algorithm in rfc793 with + * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed + * point). Adjust rtt to origin 0. + */ + delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT); + if ((tp->t_srtt += delta) <= 0) + tp->t_srtt = 1; + /* + * We accumulate a smoothed rtt variance (actually, a + * smoothed mean difference), then set the retransmit + * timer to smoothed rtt + 4 times the smoothed variance. + * rttvar is stored as fixed point with 2 bits after the + * binary point (scaled by 4). The following is + * equivalent to rfc793 smoothing with an alpha of .75 + * (rttvar = rttvar*3/4 + |delta| / 4). This replaces + * rfc793's wired-in beta. + */ + if (delta < 0) + delta = -delta; + delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT); + if ((tp->t_rttvar += delta) <= 0) + tp->t_rttvar = 1; + } else { + /* + * No rtt measurement yet - use the unsmoothed rtt. + * Set the variance to half the rtt (so our first + * retransmit happens at 3*rtt). + */ + tp->t_srtt = rtt << TCP_RTT_SHIFT; + tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1); + } + tp->t_rtt = 0; + tp->t_rxtshift = 0; + + /* + * the retransmit should happen at rtt + 4 * rttvar. + * Because of the way we do the smoothing, srtt and rttvar + * will each average +1/2 tick of bias. When we compute + * the retransmit timer, we want 1/2 tick of rounding and + * 1 extra tick because of +-1/2 tick uncertainty in the + * firing of the timer. The bias will give us exactly the + * 1.5 tick we need. But, because the bias is + * statistical, we have to test that we don't drop below + * the minimum feasible timer (which is 2 ticks). + */ + TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), + (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */ + + /* + * We received an ack for a packet that wasn't retransmitted; + * it is probably safe to discard any error indications we've + * received recently. This isn't quite right, but close enough + * for now (a route might have failed after we sent a segment, + * and the return path might not be symmetrical). + */ + tp->t_softerror = 0; +} + +/* + * Determine a reasonable value for maxseg size. + * If the route is known, check route for mtu. + * If none, use an mss that can be handled on the outgoing + * interface without forcing IP to fragment; if bigger than + * an SLIRPmbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES + * to utilize large SLIRPmbufs. If no route is found, route has no mtu, + * or the destination isn't local, use a default, hopefully conservative + * size (usually 512 or the default IP max size, but no more than the mtu + * of the interface), as we can't discover anything about intervening + * gateways or networks. We also initialize the congestion/slow start + * window to be a single segment if the destination isn't local. + * While looking at the routing entry, we also initialize other path-dependent + * parameters from pre-set or cached values in the routing entry. + */ + +int +tcp_mss(struct tcpcb *tp, u_int offer) +{ + struct SLIRPsocket *so = tp->t_socket; + int mss; + + DEBUG_CALL("tcp_mss"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("offer = %d", offer); + + mss = min(if_mtu, if_mru) - sizeof(struct tcpiphdr); + if (offer) + mss = min((u_int)mss, offer); + mss = max(mss, 32); + if (mss < tp->t_maxseg || offer != 0) + tp->t_maxseg = mss; + + tp->snd_cwnd = mss; + + sbreserve(&so->so_snd, tcp_sndspace+((tcp_sndspace%mss)?(mss-(tcp_sndspace%mss)):0)); + sbreserve(&so->so_rcv, tcp_rcvspace+((tcp_rcvspace%mss)?(mss-(tcp_rcvspace%mss)):0)); + + DEBUG_MISC((dfd, " returning mss = %d\n", mss)); + + return mss; +} diff --git a/src/tcp_output.c b/src/tcp_output.c new file mode 100644 index 0000000..e4d3652 --- /dev/null +++ b/src/tcp_output.c @@ -0,0 +1,618 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93 + * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" +#include "tcp.h" + + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +/* + * Since this is only used in "stats socket", we give meaning + * names instead of the REAL names + */ +char *tcpstates[] = { +/* "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", */ + "REDIRECT", "LISTEN", "SYN_SENT", "SYN_RCVD", + "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING", + "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT", +}; + +u_char tcp_outflags[TCP_NSTATES] = { + TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK, + TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, + TH_FIN|TH_ACK, TH_ACK, TH_ACK, +}; + + +#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */ + +/* + * Tcp output routine: figure out what should be sent and send it. + */ +int +tcp_output(struct tcpcb *tp) +{ + struct SLIRPsocket *so = tp->t_socket; + register long len, win; + int off, flags, error; + struct SLIRPmbuf *m; + struct tcpiphdr *ti; + u_char opt[MAX_TCPOPTLEN]; + unsigned optlen, hdrlen; + int idle, sendalot; + + DEBUG_CALL("tcp_output"); + DEBUG_ARG("tp = %lx", (long )tp); + + /* + * Determine length of data that should be transmitted, + * and flags that will be used. + * If there is some data or critical controls (SYN, RST) + * to send, then transmit; otherwise, investigate further. + */ + idle = (tp->snd_max == tp->snd_una); + if (idle && tp->t_idle >= tp->t_rxtcur) + /* + * We have been idle for "a while" and no acks are + * expected to clock out any data we send -- + * slow start to get ack "clock" running again. + */ + tp->snd_cwnd = tp->t_maxseg; +again: + sendalot = 0; + off = tp->snd_nxt - tp->snd_una; + win = min(tp->snd_wnd, tp->snd_cwnd); + + flags = tcp_outflags[tp->t_state]; + + DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags)); + + /* + * If in persist timeout with window of 0, send 1 byte. + * Otherwise, if window is small but nonzero + * and timer expired, we will send what we can + * and go to transmit state. + */ + if (tp->t_force) { + if (win == 0) { + /* + * If we still have some data to send, then + * clear the FIN bit. Usually this would + * happen below when it realizes that we + * aren't sending all the data. However, + * if we have exactly 1 byte of unset data, + * then it won't clear the FIN bit below, + * and if we are in persist state, we wind + * up sending the packet without recording + * that we sent the FIN bit. + * + * We can't just blindly clear the FIN bit, + * because if we don't have any more data + * to send then the probe will be the FIN + * itself. + */ + if (off < (int)so->so_snd.sb_cc) + flags &= ~TH_FIN; + win = 1; + } else { + tp->t_timer[TCPT_PERSIST] = 0; + tp->t_rxtshift = 0; + } + } + + len = min(so->so_snd.sb_cc, (u_int)win) - off; + + if (len < 0) { + /* + * If FIN has been sent but not acked, + * but we haven't been called to retransmit, + * len will be -1. Otherwise, window shrank + * after we sent into it. If window shrank to 0, + * cancel pending retransmit and pull snd_nxt + * back to (closed) window. We will enter persist + * state below. If the window didn't close completely, + * just wait for an ACK. + */ + len = 0; + if (win == 0) { + tp->t_timer[TCPT_REXMT] = 0; + tp->snd_nxt = tp->snd_una; + } + } + + if (len > tp->t_maxseg) { + len = tp->t_maxseg; + sendalot = 1; + } + if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc)) + flags &= ~TH_FIN; + + win = sbspace(&so->so_rcv); + + /* + * Sender silly window avoidance. If connection is idle + * and can send all data, a maximum segment, + * at least a maximum default-size segment do it, + * or are forced, do it; otherwise don't bother. + * If peer's buffer is tiny, then send + * when window is at least half open. + * If retransmitting (possibly after persist timer forced us + * to send into a small window), then must resend. + */ + if (len) { + if (len == tp->t_maxseg) + goto send; + if ((1 || idle || tp->t_flags & TF_NODELAY) && + (u_int)(len + off) >= so->so_snd.sb_cc) + goto send; + if (tp->t_force) + goto send; + if (len >= (long)(tp->max_sndwnd / 2) && tp->max_sndwnd > 0) + goto send; + if (SEQ_LT(tp->snd_nxt, tp->snd_max)) + goto send; + } + + /* + * Compare available window to amount of window + * known to peer (as advertised window less + * next expected input). If the difference is at least two + * max size segments, or at least 50% of the maximum possible + * window, then want to send a window update to peer. + */ + if (win > 0) { + /* + * "adv" is the amount we can increase the window, + * taking into account that we are limited by + * TCP_MAXWIN << tp->rcv_scale. + */ + long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) - + (tp->rcv_adv - tp->rcv_nxt); + + if (adv >= (long) (2 * tp->t_maxseg)) + goto send; + if (2 * adv >= (long) so->so_rcv.sb_datalen) + goto send; + } + + /* + * Send if we owe peer an ACK. + */ + if (tp->t_flags & TF_ACKNOW) + goto send; + if (flags & (TH_SYN|TH_RST)) + goto send; + if (SEQ_GT(tp->snd_up, tp->snd_una)) + goto send; + /* + * If our state indicates that FIN should be sent + * and we have not yet done so, or we're retransmitting the FIN, + * then we need to send. + */ + if (flags & TH_FIN && + ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) + goto send; + + /* + * TCP window updates are not reliable, rather a polling protocol + * using ``persist'' packets is used to insure receipt of window + * updates. The three ``states'' for the output side are: + * idle not doing retransmits or persists + * persisting to move a small or zero window + * (re)transmitting and thereby not persisting + * + * tp->t_timer[TCPT_PERSIST] + * is set when we are in persist state. + * tp->t_force + * is set when we are called to send a persist packet. + * tp->t_timer[TCPT_REXMT] + * is set when we are retransmitting + * The output side is idle when both timers are zero. + * + * If send window is too small, there is data to transmit, and no + * retransmit or persist is pending, then go to persist state. + * If nothing happens soon, send when timer expires: + * if window is nonzero, transmit what we can, + * otherwise force out a byte. + */ + if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 && + tp->t_timer[TCPT_PERSIST] == 0) { + tp->t_rxtshift = 0; + tcp_setpersist(tp); + } + + /* + * No reason to send a segment, just return. + */ + tcpstat.tcps_didnuttin++; + + return (0); + +send: + /* + * Before ESTABLISHED, force sending of initial options + * unless TCP set not to do any options. + * NOTE: we assume that the IP/TCP header plus TCP options + * always fit in a single SLIRPmbuf, leaving room for a maximum + * link header, i.e. + * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN + */ + optlen = 0; + hdrlen = sizeof (struct tcpiphdr); + if (flags & TH_SYN) { + tp->snd_nxt = tp->iss; + if ((tp->t_flags & TF_NOOPT) == 0) { + uint16_t mss; + + opt[0] = TCPOPT_MAXSEG; + opt[1] = 4; + mss = htons((uint16_t) tcp_mss(tp, 0)); + memcpy((SLIRPcaddr_t)(opt + 2), (SLIRPcaddr_t)&mss, sizeof(mss)); + optlen = 4; + +/* if ((tp->t_flags & TF_REQ_SCALE) && + * ((flags & TH_ACK) == 0 || + * (tp->t_flags & TF_RCVD_SCALE))) { + * *((uint32_t *) (opt + optlen)) = htonl( + * TCPOPT_NOP << 24 | + * TCPOPT_WINDOW << 16 | + * TCPOLEN_WINDOW << 8 | + * tp->request_r_scale); + * optlen += 4; + * } + */ + } + } + + /* + * Send a timestamp and echo-reply if this is a SYN and our side + * wants to use timestamps (TF_REQ_TSTMP is set) or both our side + * and our peer have sent timestamps in our SYN's. + */ +/* if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && + * (flags & TH_RST) == 0 && + * ((flags & (TH_SYN|TH_ACK)) == TH_SYN || + * (tp->t_flags & TF_RCVD_TSTMP))) { + * uint32_t *lp = (uint32_t *)(opt + optlen); + * + * / * Form timestamp option as shown in appendix A of RFC 1323. * / + * *lp++ = htonl(TCPOPT_TSTAMP_HDR); + * *lp++ = htonl(tcp_now); + * *lp = htonl(tp->ts_recent); + * optlen += TCPOLEN_TSTAMP_APPA; + * } + */ + hdrlen += optlen; + + /* + * Adjust data length if insertion of options will + * bump the packet length beyond the t_maxseg length. + */ + if (len > (long)(tp->t_maxseg - optlen)) { + len = tp->t_maxseg - optlen; + sendalot = 1; + } + + /* + * Grab a header SLIRPmbuf, attaching a copy of data to + * be transmitted, and initialize the header from + * the template for sends on this connection. + */ + if (len) { + if (tp->t_force && len == 1) + tcpstat.tcps_sndprobe++; + else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) { + tcpstat.tcps_sndrexmitpack++; + tcpstat.tcps_sndrexmitbyte += len; + } else { + tcpstat.tcps_sndpack++; + tcpstat.tcps_sndbyte += len; + } + + m = m_get(); + if (m == NULL) { +/* error = ENOBUFS; */ + error = 1; + goto out; + } + m->m_data += if_maxlinkhdr; + m->m_len = hdrlen; + + /* + * This will always succeed, since we make sure our SLIRPmbufs + * are big enough to hold one MSS packet + header + ... etc. + */ +/* if (len <= MHLEN - hdrlen - max_linkhdr) { */ + + sbcopy(&so->so_snd, off, (int) len, mtod(m, SLIRPcaddr_t) + hdrlen); + m->m_len += len; + +/* } else { + * m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len); + * if (m->m_next == 0) + * len = 0; + * } + */ + /* + * If we're sending everything we've got, set PUSH. + * (This will keep happy those implementations which only + * give data to the user when a buffer fills or + * a PUSH comes in.) + */ + if (off + len == so->so_snd.sb_cc) + flags |= TH_PUSH; + } else { + if (tp->t_flags & TF_ACKNOW) + tcpstat.tcps_sndacks++; + else if (flags & (TH_SYN|TH_FIN|TH_RST)) + tcpstat.tcps_sndctrl++; + else if (SEQ_GT(tp->snd_up, tp->snd_una)) + tcpstat.tcps_sndurg++; + else + tcpstat.tcps_sndwinup++; + + m = m_get(); + if (m == NULL) { +/* error = ENOBUFS; */ + error = 1; + goto out; + } + m->m_data += if_maxlinkhdr; + m->m_len = hdrlen; + } + + ti = mtod(m, struct tcpiphdr *); + + memcpy((SLIRPcaddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr)); + + /* + * Fill in fields, remembering maximum advertised + * window for use in delaying messages about window sizes. + * If resending a FIN, be sure not to use a new sequence number. + */ + if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && + tp->snd_nxt == tp->snd_max) + tp->snd_nxt--; + /* + * If we are doing retransmissions, then snd_nxt will + * not reflect the first unsent octet. For ACK only + * packets, we do not want the sequence number of the + * retransmitted packet, we want the sequence number + * of the next unsent octet. So, if there is no data + * (and no SYN or FIN), use snd_max instead of snd_nxt + * when filling in ti_seq. But if we are in persist + * state, snd_max might reflect one byte beyond the + * right edge of the window, so use snd_nxt in that + * case, since we know we aren't doing a retransmission. + * (retransmit and persist are mutually exclusive...) + */ + if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST]) + ti->ti_seq = htonl(tp->snd_nxt); + else + ti->ti_seq = htonl(tp->snd_max); + ti->ti_ack = htonl(tp->rcv_nxt); + if (optlen) { + memcpy((SLIRPcaddr_t)(ti + 1), (SLIRPcaddr_t)opt, optlen); + ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2; + } + ti->ti_flags = flags; + /* + * Calculate receive window. Don't shrink window, + * but avoid silly window syndrome. + */ + if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg) + win = 0; + if (win > (long)TCP_MAXWIN << tp->rcv_scale) + win = (long)TCP_MAXWIN << tp->rcv_scale; + if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) + win = (long)(tp->rcv_adv - tp->rcv_nxt); + ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale)); + + if (SEQ_GT(tp->snd_up, tp->snd_una)) { + ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq))); +#ifdef notdef + if (SEQ_GT(tp->snd_up, tp->snd_nxt)) { + ti->ti_urp = htons((uint16_t)(tp->snd_up - tp->snd_nxt)); +#endif + ti->ti_flags |= TH_URG; + } else + /* + * If no urgent pointer to send, then we pull + * the urgent pointer to the left edge of the send window + * so that it doesn't drift into the send window on sequence + * number wraparound. + */ + tp->snd_up = tp->snd_una; /* drag it along */ + + /* + * Put TCP length in extended header, and then + * checksum extended header and data. + */ + if (len + optlen) + ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) + + optlen + len)); + ti->ti_sum = cksum(m, (int)(hdrlen + len)); + + /* + * In transmit state, time the transmission and arrange for + * the retransmit. In persist state, just set snd_max. + */ + if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) { + tcp_seq startseq = tp->snd_nxt; + + /* + * Advance snd_nxt over sequence space of this segment. + */ + if (flags & (TH_SYN|TH_FIN)) { + if (flags & TH_SYN) + tp->snd_nxt++; + if (flags & TH_FIN) { + tp->snd_nxt++; + tp->t_flags |= TF_SENTFIN; + } + } + tp->snd_nxt += len; + if (SEQ_GT(tp->snd_nxt, tp->snd_max)) { + tp->snd_max = tp->snd_nxt; + /* + * Time this transmission if not a retransmission and + * not currently timing anything. + */ + if (tp->t_rtt == 0) { + tp->t_rtt = 1; + tp->t_rtseq = startseq; + tcpstat.tcps_segstimed++; + } + } + + /* + * Set retransmit timer if not currently set, + * and not doing an ack or a keep-alive probe. + * Initial value for retransmit timer is smoothed + * round-trip time + 2 * round-trip time variance. + * Initialize shift counter which is used for backoff + * of retransmit time. + */ + if (tp->t_timer[TCPT_REXMT] == 0 && + tp->snd_nxt != tp->snd_una) { + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + if (tp->t_timer[TCPT_PERSIST]) { + tp->t_timer[TCPT_PERSIST] = 0; + tp->t_rxtshift = 0; + } + } + } else + if (SEQ_GT(tp->snd_nxt + len, tp->snd_max)) + tp->snd_max = tp->snd_nxt + len; + + /* + * Fill in IP length and desired time to live and + * send to IP level. There should be a better way + * to handle ttl and tos; we could keep them in + * the template, but need a way to checksum without them. + */ + m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */ + + { + + ((struct ip *)ti)->ip_len = m->m_len; + + ((struct ip *)ti)->ip_ttl = ip_defttl; + ((struct ip *)ti)->ip_tos = so->so_iptos; + +/* #if BSD >= 43 */ + /* Don't do IP options... */ +/* error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route, + * so->so_options & SO_DONTROUTE, 0); + */ + error = ip_output(so, m); + +/* #else + * error = ip_output(m, (struct SLIRPmbuf *)0, &tp->t_inpcb->inp_route, + * so->so_options & SO_DONTROUTE); + * #endif + */ + } + if (error) { +out: +/* if (error == ENOBUFS) { + * tcp_quench(tp->t_inpcb, 0); + * return (0); + * } + */ +/* if ((error == EHOSTUNREACH || error == ENETDOWN) + * && TCPS_HAVERCVDSYN(tp->t_state)) { + * tp->t_softerror = error; + * return (0); + * } + */ + return (error); + } + tcpstat.tcps_sndtotal++; + + /* + * Data sent (as far as we can tell). + * If this advertises a larger window than any other segment, + * then remember the size of the advertised window. + * Any pending ACK has now been sent. + */ + if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv)) + tp->rcv_adv = tp->rcv_nxt + win; + tp->last_ack_sent = tp->rcv_nxt; + tp->t_flags &= ~(TF_ACKNOW|TF_DELACK); + if (sendalot) + goto again; + + return (0); +} + +void +tcp_setpersist(struct tcpcb *tp) +{ + int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; + +/* if (tp->t_timer[TCPT_REXMT]) + * panic("tcp_output REXMT"); + */ + /* + * Start/restart persistence timer. + */ + TCPT_RANGESET(tp->t_timer[TCPT_PERSIST], + t * tcp_backoff[tp->t_rxtshift], + TCPTV_PERSMIN, TCPTV_PERSMAX); + if (tp->t_rxtshift < TCP_MAXRXTSHIFT) + tp->t_rxtshift++; +} diff --git a/src/tcp_subr.c b/src/tcp_subr.c new file mode 100644 index 0000000..318d365 --- /dev/null +++ b/src/tcp_subr.c @@ -0,0 +1,714 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93 + * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp + */ +#ifdef _WIN32 +# include +# ifdef _DEBUG +# undef errno +# define errno (WSAGetLastError()) +# endif +#else +# include +# include +# include +# include +# ifdef _DEBUG +# include +# endif +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" +#include "tcp.h" + + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + + +/* patchable/settable parameters for tcp */ +int tcp_mssdflt = TCP_MSS; +int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; +int tcp_do_rfc1323 = 0; /* Don't do rfc1323 performance enhancements */ +int tcp_rcvspace; /* You may want to change this */ +int tcp_sndspace; /* Keep small if you have an error prone link */ + + +/* Set the socket's type of service field. */ +static const struct tos_t tcptos[] = { + { 0, 20, IPTOS_THROUGHPUT, 0 }, /* ftp data */ + { 21, 21, IPTOS_LOWDELAY, EMU_FTP }, /* ftp control */ + { 0, 23, IPTOS_LOWDELAY, 0 }, /* telnet */ + { 0, 80, IPTOS_THROUGHPUT, 0 }, /* WWW */ + { 0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */ + { 0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */ + { 0, 544, IPTOS_LOWDELAY, EMU_KSH }, /* kshell */ + { 0, 543, IPTOS_LOWDELAY, 0 }, /* klogin */ + { 0, 6667, IPTOS_THROUGHPUT, EMU_IRC }, /* IRC */ + { 0, 6668, IPTOS_THROUGHPUT, EMU_IRC }, /* IRC undernet */ + { 0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio */ + { 0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd */ + { 0, 0, 0, 0} +}; + + +/* Return TOS according to the above table. */ +uint8_t +tcp_tos(struct SLIRPsocket *so) +{ +#ifdef USE_REDIR + struct emu_t *emup; +#endif + int i = 0; + + while (tcptos[i].tos) { + if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) || + (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) { + so->so_emu = tcptos[i].emu; + return tcptos[i].tos; + } + i++; + } + +#ifdef USE_REDIR + /* Nope, lets see if there's a user-added one */ + for (emup = tcpemu; emup; emup = emup->next) { + if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) || + (emup->lport && (ntohs(so->so_lport) == emup->lport))) { + so->so_emu = emup->emu; + return emup->tos; + } + } +#endif + + return 0; +} + + +/* TCP initialization. */ +void +tcp_init(void) +{ + tcp_iss = 1; /* wrong */ + tcb.so_next = tcb.so_prev = &tcb; + + /* tcp_rcvspace = our Window we advertise to the remote */ + tcp_rcvspace = TCP_RCVSPACE; + tcp_sndspace = TCP_SNDSPACE; + + /* Make sure tcp_sndspace is at least 2*MSS */ + if (tcp_sndspace < (int)(2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr)))) + tcp_sndspace = (int)(2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr))); +} + + +/* + * Create template to be used to send tcp packets on a connection. + * Call after host entry created, fills + * in a skeletal tcp/ip header, minimizing the amount of work + * necessary when the connection is used. + */ +/* struct tcpiphdr * */ +void +tcp_template(struct tcpcb *tp) +{ + struct SLIRPsocket *so = tp->t_socket; + struct tcpiphdr *n = &tp->t_template; + + n->ti_next = n->ti_prev = 0; + n->ti_x1 = 0; + n->ti_pr = IPPROTO_TCP; + n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); + n->ti_src = so->so_faddr; + n->ti_dst = so->so_laddr; + n->ti_sport = so->so_fport; + n->ti_dport = so->so_lport; + + n->ti_seq = 0; + n->ti_ack = 0; + n->ti_x2 = 0; + n->ti_off = 5; + n->ti_flags = 0; + n->ti_win = 0; + n->ti_sum = 0; + n->ti_urp = 0; +} + + +/* + * Send a single message to the TCP at address specified by + * the given TCP/IP header. If m == 0, then we make a copy + * of the tcpiphdr at ti and send directly to the addressed host. + * This is used to force keep alive messages out using the TCP + * template for a connection tp->t_template. If flags are given + * then we send a message back to the TCP which originated the + * segment ti, and discard the SLIRPmbuf containing it and any other + * attached SLIRPmbufs. + * + * In any case the ack and sequence number of the transmitted + * segment are as specified by the parameters. + */ +void +tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct SLIRPmbuf *m, tcp_seq ack, tcp_seq seq, int flags) +{ + register int tlen; + int win = 0; + + DEBUG_CALL("tcp_respond"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("ti = %lx", (long)ti); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("ack = %u", ack); + DEBUG_ARG("seq = %u", seq); + DEBUG_ARG("flags = %x", flags); + + if (tp) + win = sbspace(&tp->t_socket->so_rcv); + if (m == 0) { + if ((m = m_get()) == NULL) + return; +#ifdef TCP_COMPAT_42 + tlen = 1; +#else + tlen = 0; +#endif + m->m_data += if_maxlinkhdr; + *mtod(m, struct tcpiphdr *) = *ti; + ti = mtod(m, struct tcpiphdr *); + flags = TH_ACK; + } else { + /* + * ti points into m so the next line is just making + * the SLIRPmbuf point to ti + */ + m->m_data = (SLIRPcaddr_t)ti; + + m->m_len = sizeof (struct tcpiphdr); + tlen = 0; +#define xchg(a,b,type) { type t; t=a; a=b; b=t; } + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); +#undef xchg + } + ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); + tlen += sizeof (struct tcpiphdr); + m->m_len = tlen; + + ti->ti_next = ti->ti_prev = 0; + ti->ti_x1 = 0; + ti->ti_seq = htonl(seq); + ti->ti_ack = htonl(ack); + ti->ti_x2 = 0; + ti->ti_off = sizeof (struct tcphdr) >> 2; + ti->ti_flags = flags; + if (tp) + ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale)); + else + ti->ti_win = htons((uint16_t)win); + ti->ti_urp = 0; + ti->ti_sum = 0; + ti->ti_sum = cksum(m, tlen); + ((struct ip *)ti)->ip_len = tlen; + + if (flags & TH_RST) + ((struct ip *)ti)->ip_ttl = MAXTTL; + else + ((struct ip *)ti)->ip_ttl = ip_defttl; + + (void) ip_output((struct SLIRPsocket *)0, m); +} + + +/* + * Create a new TCP control block, making an + * empty reassembly queue and hooking it to the argument + * protocol control block. + */ +struct tcpcb * +tcp_newtcpcb(struct SLIRPsocket *so) +{ + struct tcpcb *tp; + + tp = (struct tcpcb *)malloc(sizeof(*tp)); + if (tp == NULL) + return ((struct tcpcb *)0); + + memset((char *) tp, 0, sizeof(struct tcpcb)); + tp->seg_next = tp->seg_prev = (tcpiphdrp_32)tp; + tp->t_maxseg = tcp_mssdflt; + + tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0; + tp->t_socket = so; + + /* + * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no + * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives + * reasonable initial retransmit time. + */ + tp->t_srtt = TCPTV_SRTTBASE; + tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2; + tp->t_rttmin = TCPTV_MIN; + + TCPT_RANGESET(tp->t_rxtcur, + ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1, + TCPTV_MIN, TCPTV_REXMTMAX); + + tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; + tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; + tp->t_state = TCPS_CLOSED; + + so->so_tcpcb = tp; + + return (tp); +} + + +/* + * Drop a TCP connection, reporting the specified error. + * If connection is synchronized, then send a RST to peer. + */ +struct tcpcb * +tcp_drop(struct tcpcb *tp, int err) +{ + DEBUG_CALL("tcp_drop"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("errno = %d", errno); + + if (TCPS_HAVERCVDSYN(tp->t_state)) { + tp->t_state = TCPS_CLOSED; + (void) tcp_output(tp); + tcpstat.tcps_drops++; + } else + tcpstat.tcps_conndrops++; + +#if 0 + if (errno == ETIMEDOUT && tp->t_softerror) + errno = tp->t_softerror; + + so->so_error = errno; +#endif + + return (tcp_close(tp)); +} + + +/* + * Close a TCP control block: + * discard all space held by the tcp + * discard internet protocol block + * wake up any sleepers + */ +struct tcpcb * +tcp_close(struct tcpcb *tp) +{ + struct tcpiphdr *t; + struct SLIRPsocket *so = tp->t_socket; + struct SLIRPmbuf *m; + + DEBUG_CALL("tcp_close"); + DEBUG_ARG("tp = %lx", (long )tp); + + /* free the reassembly queue, if any */ + t = (struct tcpiphdr *) tp->seg_next; + while (t != (struct tcpiphdr *)tp) { + t = (struct tcpiphdr *)t->ti_next; + m = (struct SLIRPmbuf *) REASS_MBUF((struct tcpiphdr *)t->ti_prev); + remque_32((struct tcpiphdr *) t->ti_prev); + m_freem(m); + } + + /* It's static */ +#if 0 + if (tp->t_template) + (void) m_free(dtom(tp->t_template)); + + free(tp, M_PCB); +#endif + free(tp); + + so->so_tcpcb = 0; + soisfdisconnected(so); + + /* clobber input socket cache if we're closing the cached connection */ + if (so == tcp_last_so) + tcp_last_so = &tcb; + closesocket(so->s); + + sbfree(&so->so_rcv); + sbfree(&so->so_snd); + sofree(so); + tcpstat.tcps_closed++; + + return ((struct tcpcb *)0); +} + + +void +tcp_drain(void) +{ + /* XXX */ +} + + +/* + * When a source quench is received, close congestion window + * to one segment. We will gradually open it again as we proceed. + */ +#ifdef notdef +void +tcp_quench(int i, int errno) +{ + struct tcpcb *tp = intotcpcb(inp); + + if (tp) + tp->snd_cwnd = tp->t_maxseg; +} +#endif + + +/* + * TCP protocol interface to socket abstraction. + */ + +/* + * User issued close, and wish to trail through shutdown states: + * if never received SYN, just forget it. If got a SYN from peer, + * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. + * If already got a FIN from peer, then almost done; go to LAST_ACK + * state. In all other cases, have already sent FIN to peer (e.g. + * after PRU_SHUTDOWN), and just have to play tedious game waiting + * for peer to send FIN or not respond to keep-alives, etc. + * We can let the user exit from the close as soon as the FIN is acked. + */ +void +tcp_sockclosed(struct tcpcb *tp) +{ + + DEBUG_CALL("tcp_sockclosed"); + DEBUG_ARG("tp = %lx", (long)tp); + + switch (tp->t_state) { + + case TCPS_CLOSED: + case TCPS_LISTEN: + case TCPS_SYN_SENT: + tp->t_state = TCPS_CLOSED; + tp = tcp_close(tp); + break; + + case TCPS_SYN_RECEIVED: + case TCPS_ESTABLISHED: + tp->t_state = TCPS_FIN_WAIT_1; + break; + + case TCPS_CLOSE_WAIT: + tp->t_state = TCPS_LAST_ACK; + break; + } +/* soisfdisconnecting(tp->t_socket); */ + if (tp && tp->t_state >= TCPS_FIN_WAIT_2) + soisfdisconnected(tp->t_socket); + if (tp) + tcp_output(tp); +} + +/* + * Connect to a host on the Internet + * Called by tcp_input + * Only do a connect, the tcp fields will be set in tcp_input + * return 0 if there's a result of the connect, + * else return -1 means we're still connecting + * The return value is almost always -1 since the socket is + * nonblocking. Connect returns after the SYN is sent, and does + * not wait for ACK+SYN. + */ +int tcp_fconnect(struct SLIRPsocket *so) +{ + int ret=0; + + DEBUG_CALL("tcp_fconnect"); + DEBUG_ARG("so = %lx", (long )so); + + if( (ret=so->s=socket(AF_INET,SOCK_STREAM,0)) >= 0) { + int opt, s=so->s; + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + + fd_nonblock(s); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt )); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt )); + + addr.sin_family = AF_INET; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + /* It's an alias */ + switch(ntohl(so->so_faddr.s_addr) & 0xff) { + case CTL_SERVER: + addr.sin_addr = dns_addr; + break; + case CTL_ROUTER: + default: + addr.sin_addr = loopback_addr; + break; + } + } else + addr.sin_addr = so->so_faddr; + addr.sin_port = so->so_fport; + + DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, " + "addr.sin_addr.s_addr=%.16s\n", + ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + /* We don't care what port we get */ + ret = connect(s,(struct sockaddr *)&addr,sizeof (addr)); + + /* + * If it's not in progress, it failed, so we just return 0, + * without clearing SS_NOFDREF + */ + soisfconnecting(so); + } + + return(ret); +} + +/* + * Accept the socket and connect to the local-host + * + * We have a problem. The correct thing to do would be + * to first connect to the local-host, and only if the + * connection is accepted, then do an accept() here. + * But, a) we need to know who's trying to connect + * to the socket to be able to SYN the local-host, and + * b) we are already connected to the foreign host by + * the time it gets to accept(), so... We simply accept + * here and SYN the local-host. + */ +void +tcp_connect(struct SLIRPsocket *inso) +{ + struct SLIRPsocket *so; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + struct tcpcb *tp; + int s, opt; + + DEBUG_CALL("tcp_connect"); + DEBUG_ARG("inso = %lx", (long)inso); + + /* + * If it's an SS_ACCEPTONCE socket, no need to socreate() + * another socket, just use the accept() socket. + */ + if (inso->so_state & SS_FACCEPTONCE) { + /* FACCEPTONCE already have a tcpcb */ + so = inso; + } else { + if ((so = socreate()) == NULL) { + /* If it failed, get rid of the pending connection */ + closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen)); + return; + } + if (tcp_attach(so) < 0) { + free(so); /* NOT sofree */ + return; + } + so->so_laddr = inso->so_laddr; + so->so_lport = inso->so_lport; + } + + (void) tcp_mss(sototcpcb(so), 0); + + if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) { + tcp_close(sototcpcb(so)); /* This will sofree() as well */ + return; + } + fd_nonblock(s); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); + opt = 1; + setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int)); + + so->so_fport = addr.sin_port; + so->so_faddr = addr.sin_addr; + /* Translate connections from localhost to the real hostname */ + if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr) + so->so_faddr = router_addr; + + /* Close the accept() socket, set right state */ + if (inso->so_state & SS_FACCEPTONCE) { + closesocket(so->s); /* If we only accept once, close the accept() socket */ + so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */ + /* if it's not FACCEPTONCE, it's already NOFDREF */ + } + so->s = s; + + so->so_iptos = tcp_tos(so); + tp = sototcpcb(so); + + tcp_template(tp); + + /* Compute window scaling to request. */ +/* while (tp->request_r_scale < TCP_MAX_WINSHIFT && + * (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) + * tp->request_r_scale++; + */ + +/* soisconnecting(so); */ /* NOFDREF used instead */ + tcpstat.tcps_connattempt++; + + tp->t_state = TCPS_SYN_SENT; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->iss = tcp_iss; + tcp_iss += TCP_ISSINCR/2; + tcp_sendseqinit(tp); + tcp_output(tp); +} + +/* + * Attach a TCPCB to a socket. + */ +int +tcp_attach(struct SLIRPsocket *so) +{ + if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) + return -1; + + insque(so, &tcb); + + return 0; +} + + +/* + * Do misc. config of SLiRP while its running. + * Return 0 if this connections is to be closed, 1 otherwise, + * return 2 if this is a command-line connection + */ +int +tcp_ctl(struct SLIRPsocket *so) +{ + struct sbuf *sb = &so->so_snd; +#if 0 + struct SLIRPsocket *tmpso; +#endif +#ifdef USE_REDIR + struct ex_list *ex_ptr; + int do_pty; +#endif + int command; + + DEBUG_CALL("tcp_ctl"); + DEBUG_ARG("so = %lx", (long )so); + +#if 0 + /* + * Check if they're authorised + */ + if (ctl_addr.s_addr && (ctl_addr.s_addr == -1 || (so->so_laddr.s_addr != ctl_addr.s_addr))) { + sb->sb_cc = sprintf(sb->sb_wptr,"Error: Permission denied.\r\n"); + sb->sb_wptr += sb->sb_cc; + return 0; + } +#endif + command = (ntohl(so->so_faddr.s_addr) & 0xff); + + switch(command) { + default: /* Check for exec's */ +#ifdef USE_REDIR + /* + * Check if it's pty_exec + */ + for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_fport == so->so_fport && + command == ex_ptr->ex_addr) { + do_pty = ex_ptr->ex_pty; + goto do_exec; + } + } +#endif + + /* + * Nothing bound.. + */ + /* tcp_fconnect(so); */ + + /* FALLTHROUGH */ + case CTL_ROUTER: + sb->sb_cc = sprintf(sb->sb_wptr, + "Error: No application configured.\r\n"); + sb->sb_wptr += sb->sb_cc; + return(0); + +#ifdef USE_REDIR + do_exec: + DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec)); + return(fork_exec(so, ex_ptr->ex_exec, do_pty)); +#endif + +#if 0 + case CTL_CMD: + for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) { + if (tmpso->so_emu == EMU_CTL && + !(tmpso->so_tcpcb? + (tmpso->so_tcpcb->t_state & (TCPS_TIME_WAIT|TCPS_LAST_ACK)) + :0)) { + /* Ooops, control connection already active */ + sb->sb_cc = sprintf(sb->sb_wptr,"Sorry, already connected.\r\n"); + sb->sb_wptr += sb->sb_cc; + return 0; + } + } + so->so_emu = EMU_CTL; + ctl_password_ok = 0; + sb->sb_cc = sprintf(sb->sb_wptr, "Slirp command-line ready (type \"help\" for help).\r\nSlirp> "); + sb->sb_wptr += sb->sb_cc; + do_echo=-1; + return(2); +#endif + } +} diff --git a/src/tcp_timer.c b/src/tcp_timer.c new file mode 100644 index 0000000..7ebfce8 --- /dev/null +++ b/src/tcp_timer.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93 + * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp + */ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "socket.h" +#include "mbuf.h" +#include "ip.h" +#include "tcp.h" + + +int tcp_keepidle = TCPTV_KEEP_IDLE; +int tcp_keepintvl = TCPTV_KEEPINTVL; +int tcp_maxidle; +int so_options = DO_KEEPALIVE; + +struct tcpstat tcpstat; /* tcp statistics */ +uint32_t tcp_now; /* for RFC 1323 timestamps */ + +/* + * Fast timeout routine for processing delayed acks + */ +void +tcp_fasttimo() +{ + register struct SLIRPsocket *so; + register struct tcpcb *tp; + + DEBUG_CALL("tcp_fasttimo"); + + so = tcb.so_next; + if (so) + for (; so != &tcb; so = so->so_next) + if ((tp = (struct tcpcb *)so->so_tcpcb) && + (tp->t_flags & TF_DELACK)) { + tp->t_flags &= ~TF_DELACK; + tp->t_flags |= TF_ACKNOW; + tcpstat.tcps_delack++; + (void) tcp_output(tp); + } +} + +/* + * Tcp protocol timeout routine called every 500 ms. + * Updates the timers in all active tcb's and + * causes finite state machine actions if timers expire. + */ +void +tcp_slowtimo() +{ + register struct SLIRPsocket *ip, *ipnxt; + register struct tcpcb *tp; + register int i; + + DEBUG_CALL("tcp_slowtimo"); + + tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl; + /* + * Search through tcb's and update active timers. + */ + ip = tcb.so_next; + if (ip == 0) + return; + for (; ip != &tcb; ip = ipnxt) { + ipnxt = ip->so_next; + tp = sototcpcb(ip); + if (tp == 0) + continue; + for (i = 0; i < TCPT_NTIMERS; i++) { + if (tp->t_timer[i] && --tp->t_timer[i] == 0) { + tcp_timers(tp,i); + if (ipnxt->so_prev != ip) + goto tpgone; + } + } + tp->t_idle++; + if (tp->t_rtt) + tp->t_rtt++; +tpgone: + ; + } + tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */ +#ifdef TCP_COMPAT_42 + if ((int)tcp_iss < 0) + tcp_iss = 0; /* XXX */ +#endif + tcp_now++; /* for timestamps */ +} + +/* + * Cancel all timers for TCP tp. + */ +void +tcp_canceltimers(struct tcpcb *tp) +{ + register int i; + + for (i = 0; i < TCPT_NTIMERS; i++) + tp->t_timer[i] = 0; +} + +int tcp_backoff[TCP_MAXRXTSHIFT + 1] = + { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; + +/* + * TCP timer processing. + */ +struct tcpcb * +tcp_timers(struct tcpcb *tp, int timer) +{ + int rexmt; + + DEBUG_CALL("tcp_timers"); + + switch (timer) { + + /* + * 2 MSL timeout in shutdown went off. If we're closed but + * still waiting for peer to close and connection has been idle + * too long, or if 2MSL time is up from TIME_WAIT, delete connection + * control block. Otherwise, check again in a bit. + */ + case TCPT_2MSL: + if (tp->t_state != TCPS_TIME_WAIT && + tp->t_idle <= tcp_maxidle) + tp->t_timer[TCPT_2MSL] = tcp_keepintvl; + else + tp = tcp_close(tp); + break; + + /* + * Retransmission timer went off. Message has not + * been acked within retransmit interval. Back off + * to a longer retransmit interval and retransmit one segment. + */ + case TCPT_REXMT: + + /* + * XXXXX If a packet has timed out, then remove all the queued + * packets for that session. + */ + + if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { + /* + * This is a hack to suit our terminal server here at the uni of canberra + * since they have trouble with zeroes... It usually lets them through + * unharmed, but under some conditions, it'll eat the zeros. If we + * keep retransmitting it, it'll keep eating the zeroes, so we keep + * retransmitting, and eventually the connection dies... + * (this only happens on incoming data) + * + * So, if we were gonna drop the connection from too many retransmits, + * don't... instead halve the t_maxseg, which might break up the NULLs and + * let them through + * + * *sigh* + */ + + tp->t_maxseg >>= 1; + if (tp->t_maxseg < 32) { + /* + * We tried our best, now the connection must die! + */ + tp->t_rxtshift = TCP_MAXRXTSHIFT; + tcpstat.tcps_timeoutdrop++; + tp = tcp_drop(tp, tp->t_softerror); + /* tp->t_softerror : ETIMEDOUT); */ /* XXX */ + return (tp); /* XXX */ + } + + /* + * Set rxtshift to 6, which is still at the maximum + * backoff time + */ + tp->t_rxtshift = 6; + } + tcpstat.tcps_rexmttimeo++; + rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; + TCPT_RANGESET(tp->t_rxtcur, rexmt, + (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */ + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + /* + * If losing, let the lower level know and try for + * a better route. Also, if we backed off this far, + * our srtt estimate is probably bogus. Clobber it + * so we'll take the next rtt measurement as our srtt; + * move the current srtt into rttvar to keep the current + * retransmit times until then. + */ + if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { +/* in_losing(tp->t_inpcb); */ + tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); + tp->t_srtt = 0; + } + tp->snd_nxt = tp->snd_una; + /* + * If timing a segment in this window, stop the timer. + */ + tp->t_rtt = 0; + /* + * Close the congestion window down to one segment + * (we'll open it by one segment for each ack we get). + * Since we probably have a window's worth of unacked + * data accumulated, this "slow start" keeps us from + * dumping all that data as back-to-back packets (which + * might overwhelm an intermediate gateway). + * + * There are two phases to the opening: Initially we + * open by one mss on each ack. This makes the window + * size increase exponentially with time. If the + * window is larger than the path can handle, this + * exponential growth results in dropped packet(s) + * almost immediately. To get more time between + * drops but still "push" the network to take advantage + * of improving conditions, we switch from exponential + * to linear window opening at some threshold size. + * For a threshold, we use half the current window + * size, truncated to a multiple of the mss. + * + * (the minimum cwnd that will give us exponential + * growth is 2 mss. We don't allow the threshold + * to go below this.) + */ + { + u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; + if (win < 2) + win = 2; + tp->snd_cwnd = tp->t_maxseg; + tp->snd_ssthresh = win * tp->t_maxseg; + tp->t_dupacks = 0; + } + (void) tcp_output(tp); + break; + + /* + * Persistence timer into zero window. + * Force a byte to be output, if possible. + */ + case TCPT_PERSIST: + tcpstat.tcps_persisttimeo++; + tcp_setpersist(tp); + tp->t_force = 1; + (void) tcp_output(tp); + tp->t_force = 0; + break; + + /* + * Keep-alive timer went off; send something + * or drop connection if idle for too long. + */ + case TCPT_KEEP: + tcpstat.tcps_keeptimeo++; + if (tp->t_state < TCPS_ESTABLISHED) + goto dropit; + +/* if (tp->t_socket->so_options & SO_KEEPALIVE && */ + if ((so_options) && tp->t_state <= TCPS_CLOSE_WAIT) { + if (tp->t_idle >= tcp_keepidle + tcp_maxidle) + goto dropit; + /* + * Send a packet designed to force a response + * if the peer is up and reachable: + * either an ACK if the connection is still alive, + * or an RST if the peer has closed the connection + * due to timeout or reboot. + * Using sequence number tp->snd_una-1 + * causes the transmitted zero-length segment + * to lie outside the receive window; + * by the protocol spec, this requires the + * correspondent TCP to respond. + */ + tcpstat.tcps_keepprobe++; +#ifdef TCP_COMPAT_42 + /* + * The keepalive packet must have nonzero length + * to get a 4.2 host to respond. + */ + tcp_respond(tp, &tp->t_template, (struct SLIRPmbuf *)NULL, + tp->rcv_nxt - 1, tp->snd_una - 1, 0); +#else + tcp_respond(tp, &tp->t_template, (struct SLIRPmbuf *)NULL, + tp->rcv_nxt, tp->snd_una - 1, 0); +#endif + tp->t_timer[TCPT_KEEP] = tcp_keepintvl; + } else + tp->t_timer[TCPT_KEEP] = tcp_keepidle; + break; + + dropit: + tcpstat.tcps_keepdrops++; + tp = tcp_drop(tp, 0); /* ETIMEDOUT); */ + break; + } + + return (tp); +} diff --git a/src/tcp_timer.h b/src/tcp_timer.h new file mode 100644 index 0000000..1ccebfa --- /dev/null +++ b/src/tcp_timer.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93 + * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp + */ +#ifndef _TCP_TIMER_H_ +#define _TCP_TIMER_H_ + +/* + * Definitions of the TCP timers. These timers are counted + * down PR_SLOWHZ times a second. + */ +#define TCPT_NTIMERS 4 + +#define TCPT_REXMT 0 /* retransmit */ +#define TCPT_PERSIST 1 /* retransmit persistence */ +#define TCPT_KEEP 2 /* keep alive */ +#define TCPT_2MSL 3 /* 2*msl quiet time timer */ + +/* + * The TCPT_REXMT timer is used to force retransmissions. + * The TCP has the TCPT_REXMT timer set whenever segments + * have been sent for which ACKs are expected but not yet + * received. If an ACK is received which advances tp->snd_una, + * then the retransmit timer is cleared (if there are no more + * outstanding segments) or reset to the base value (if there + * are more ACKs expected). Whenever the retransmit timer goes off, + * we retransmit one unacknowledged segment, and do a backoff + * on the retransmit timer. + * + * The TCPT_PERSIST timer is used to keep window size information + * flowing even if the window goes shut. If all previous transmissions + * have been acknowledged (so that there are no retransmissions in progress), + * and the window is too small to bother sending anything, then we start + * the TCPT_PERSIST timer. When it expires, if the window is nonzero, + * we go to transmit state. Otherwise, at intervals send a single byte + * into the peer's window to force him to update our window information. + * We do this at most as often as TCPT_PERSMIN time intervals, + * but no more frequently than the current estimate of round-trip + * packet time. The TCPT_PERSIST timer is cleared whenever we receive + * a window update from the peer. + * + * The TCPT_KEEP timer is used to keep connections alive. If an + * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time, + * but not yet established, then we drop the connection. Once the connection + * is established, if the connection is idle for TCPTV_KEEP_IDLE time + * (and keepalives have been enabled on the socket), we begin to probe + * the connection. We force the peer to send us a segment by sending: + * + * This segment is (deliberately) outside the window, and should elicit + * an ack segment in response from the peer. If, despite the TCPT_KEEP + * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE + * amount of time probing, then we drop the connection. + */ + +/* + * Time constants. + */ +#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */ + +#define TCPTV_SRTTBASE 0 /* base roundtrip time; + if 0, no idea yet */ +#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */ + +#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */ +#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */ + +#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */ +#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */ +#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */ +#define TCPTV_KEEPCNT 8 /* max probes before drop */ + +#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */ +/* #define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) */ /* max allowable REXMT value */ +#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */ + +#define TCP_LINGERTIME 120 /* linger at most 2 minutes */ + +#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */ + + +#ifdef TCPTIMERS +char *tcptimers[] = + { "REXMT", "PERSIST", "KEEP", "2MSL" }; +#endif + +/* + * Force a time value to be in a certain range. + */ +#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \ + (tv) = (value); \ + if ((tv) < (tvmin)) \ + (tv) = (tvmin); \ + else if ((tv) > (tvmax)) \ + (tv) = (tvmax); \ +} + +extern int tcp_keepidle; /* time before keepalive probes begin */ +extern int tcp_keepintvl; /* time between keepalive probes */ +extern int tcp_maxidle; /* time to drop after starting probes */ +extern int tcp_ttl; /* time to live for TCP segs */ +extern int tcp_backoff[]; + +struct tcpcb; + +extern void tcp_fasttimo(void); +extern void tcp_slowtimo(void); +extern void tcp_canceltimers(struct tcpcb *); +extern struct tcpcb * tcp_timers(register struct tcpcb *, int); + + +#endif diff --git a/src/tcp_var.h b/src/tcp_var.h new file mode 100644 index 0000000..7c25a3b --- /dev/null +++ b/src/tcp_var.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 1982, 1986, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94 + * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp + */ + +#ifndef _TCP_VAR_H_ +#define _TCP_VAR_H_ + +#include "tcpip.h" +#include "tcp_timer.h" + +#ifdef __amd64__ +typedef uintptr_t tcpiphdrp_32; +#else +#if SIZEOF_CHAR_P == 4 + typedef struct tcpiphdr *tcpiphdrp_32; +#else + typedef uint32_t tcpiphdrp_32; +#endif +#endif + +/* + * Tcp control block, one per tcp; fields: + */ +struct tcpcb { + tcpiphdrp_32 seg_next; /* sequencing queue */ + tcpiphdrp_32 seg_prev; + short t_state; /* state of this connection */ + short t_timer[TCPT_NTIMERS]; /* tcp timers */ + short t_rxtshift; /* log(2) of rexmt exp. backoff */ + short t_rxtcur; /* current retransmit value */ + short t_dupacks; /* consecutive dup acks recd */ + uint16_t t_maxseg; /* maximum segment size */ + char t_force; /* 1 if forcing out a byte */ + uint16_t t_flags; +#define TF_ACKNOW 0x0001 /* ack peer immediately */ +#define TF_DELACK 0x0002 /* ack, but try to delay it */ +#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */ +#define TF_NOOPT 0x0008 /* don't use tcp options */ +#define TF_SENTFIN 0x0010 /* have sent FIN */ +#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */ +#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */ +#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */ +#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */ +#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */ + + /* Make it static for now */ +/* struct tcpiphdr *t_template; / * skeletal packet for transmit */ + struct tcpiphdr t_template; + + struct SLIRPsocket *t_socket; /* back pointer to socket */ +/* + * The following fields are used as in the protocol specification. + * See RFC783, Dec. 1981, page 21. + */ +/* send sequence variables */ + tcp_seq snd_una; /* send unacknowledged */ + tcp_seq snd_nxt; /* send next */ + tcp_seq snd_up; /* send urgent pointer */ + tcp_seq snd_wl1; /* window update seg seq number */ + tcp_seq snd_wl2; /* window update seg ack number */ + tcp_seq iss; /* initial send sequence number */ + uint32_t snd_wnd; /* send window */ +/* receive sequence variables */ + uint32_t rcv_wnd; /* receive window */ + tcp_seq rcv_nxt; /* receive next */ + tcp_seq rcv_up; /* receive urgent pointer */ + tcp_seq irs; /* initial receive sequence number */ +/* + * Additional variables for this implementation. + */ +/* receive variables */ + tcp_seq rcv_adv; /* advertised window */ +/* retransmit variables */ + tcp_seq snd_max; /* highest sequence number sent; + * used to recognize retransmits + */ +/* congestion control (for slow start, source quench, retransmit after loss) */ + uint32_t snd_cwnd; /* congestion-controlled window */ + uint32_t snd_ssthresh; /* snd_cwnd size threshold for + * for slow start exponential to + * linear switch + */ +/* + * transmit timing stuff. See below for scale of srtt and rttvar. + * "Variance" is actually smoothed difference. + */ + short t_idle; /* inactivity time */ + short t_rtt; /* round trip time */ + tcp_seq t_rtseq; /* sequence number being timed */ + short t_srtt; /* smoothed round-trip time */ + short t_rttvar; /* variance in round-trip time */ + uint16_t t_rttmin; /* minimum rtt allowed */ + uint32_t max_sndwnd; /* largest window peer has offered */ + +/* out-of-band data */ + char t_oobflags; /* have some */ + char t_iobc; /* input character */ +#define TCPOOB_HAVEDATA 0x01 +#define TCPOOB_HADDATA 0x02 + short t_softerror; /* possible error not yet reported */ + +/* RFC 1323 variables */ + u_char snd_scale; /* window scaling for send window */ + u_char rcv_scale; /* window scaling for recv window */ + u_char request_r_scale; /* pending window scaling */ + u_char requested_s_scale; + uint32_t ts_recent; /* timestamp echo data */ + uint32_t ts_recent_age; /* when last updated */ + tcp_seq last_ack_sent; + +}; + +#define sototcpcb(so) ((so)->so_tcpcb) + +/* + * The smoothed round-trip time and estimated variance + * are stored as fixed point numbers scaled by the values below. + * For convenience, these scales are also used in smoothing the average + * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed). + * With these scales, srtt has 3 bits to the right of the binary point, + * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the + * binary point, and is smoothed with an ALPHA of 0.75. + */ +#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */ +#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */ +#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */ +#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */ + +/* + * The initial retransmission should happen at rtt + 4 * rttvar. + * Because of the way we do the smoothing, srtt and rttvar + * will each average +1/2 tick of bias. When we compute + * the retransmit timer, we want 1/2 tick of rounding and + * 1 extra tick because of +-1/2 tick uncertainty in the + * firing of the timer. The bias will give us exactly the + * 1.5 tick we need. But, because the bias is + * statistical, we have to test that we don't drop below + * the minimum feasible timer (which is 2 ticks). + * This macro assumes that the value of TCP_RTTVAR_SCALE + * is the same as the multiplier for rttvar. + */ +#define TCP_REXMTVAL(tp) \ + (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar) + +/* XXX + * We want to avoid doing m_pullup on incoming packets but that + * means avoiding dtom on the tcp reassembly code. That in turn means + * keeping an mbuf pointer in the reassembly queue (since we might + * have a cluster). As a quick hack, the source & destination + * port numbers (which are no longer needed once we've located the + * tcpcb) are overlayed with an mbuf pointer. + */ +#ifdef __amd64__ +typedef uintptr_t mbufp_32; +#else +#if SIZEOF_CHAR_P == 4 +typedef struct SLIRPmbuf *mbufp_32; +#else +typedef uint32_t mbufp_32; +#endif +#endif +#define REASS_MBUF(ti) (*(mbufp_32 *)&((ti)->ti_t)) + +/* + * TCP statistics. + * Many of these should be kept per connection, + * but that's inconvenient at the moment. + */ +struct tcpstat { + uint32_t tcps_connattempt; /* connections initiated */ + uint32_t tcps_accepts; /* connections accepted */ + uint32_t tcps_connects; /* connections established */ + uint32_t tcps_drops; /* connections dropped */ + uint32_t tcps_conndrops; /* embryonic connections dropped */ + uint32_t tcps_closed; /* conn. closed (includes drops) */ + uint32_t tcps_segstimed; /* segs where we tried to get rtt */ + uint32_t tcps_rttupdated; /* times we succeeded */ + uint32_t tcps_delack; /* delayed acks sent */ + uint32_t tcps_timeoutdrop; /* conn. dropped in rxmt timeout */ + uint32_t tcps_rexmttimeo; /* retransmit timeouts */ + uint32_t tcps_persisttimeo; /* persist timeouts */ + uint32_t tcps_keeptimeo; /* keepalive timeouts */ + uint32_t tcps_keepprobe; /* keepalive probes sent */ + uint32_t tcps_keepdrops; /* connections dropped in keepalive */ + + uint32_t tcps_sndtotal; /* total packets sent */ + uint32_t tcps_sndpack; /* data packets sent */ + uint32_t tcps_sndbyte; /* data bytes sent */ + uint32_t tcps_sndrexmitpack; /* data packets retransmitted */ + uint32_t tcps_sndrexmitbyte; /* data bytes retransmitted */ + uint32_t tcps_sndacks; /* ack-only packets sent */ + uint32_t tcps_sndprobe; /* window probes sent */ + uint32_t tcps_sndurg; /* packets sent with URG only */ + uint32_t tcps_sndwinup; /* window update-only packets sent */ + uint32_t tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */ + + uint32_t tcps_rcvtotal; /* total packets received */ + uint32_t tcps_rcvpack; /* packets received in sequence */ + uint32_t tcps_rcvbyte; /* bytes received in sequence */ + uint32_t tcps_rcvbadsum; /* packets received with ccksum errs */ + uint32_t tcps_rcvbadoff; /* packets received with bad offset */ +/* uint32_t tcps_rcvshort; */ /* packets received too short */ + uint32_t tcps_rcvduppack; /* duplicate-only packets received */ + uint32_t tcps_rcvdupbyte; /* duplicate-only bytes received */ + uint32_t tcps_rcvpartduppack; /* packets with some duplicate data */ + uint32_t tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */ + uint32_t tcps_rcvoopack; /* out-of-order packets received */ + uint32_t tcps_rcvoobyte; /* out-of-order bytes received */ + uint32_t tcps_rcvpackafterwin; /* packets with data after window */ + uint32_t tcps_rcvbyteafterwin; /* bytes rcvd after window */ + uint32_t tcps_rcvafterclose; /* packets rcvd after "close" */ + uint32_t tcps_rcvwinprobe; /* rcvd window probe packets */ + uint32_t tcps_rcvdupack; /* rcvd duplicate acks */ + uint32_t tcps_rcvacktoomuch; /* rcvd acks for unsent data */ + uint32_t tcps_rcvackpack; /* rcvd ack packets */ + uint32_t tcps_rcvackbyte; /* bytes acked by rcvd acks */ + uint32_t tcps_rcvwinupd; /* rcvd window update packets */ +/* uint32_t tcps_pawsdrop; */ /* segments dropped due to PAWS */ + uint32_t tcps_predack; /* times hdr predict ok for acks */ + uint32_t tcps_preddat; /* times hdr predict ok for data pkts */ + uint32_t tcps_socachemiss; /* tcp_last_so misses */ + uint32_t tcps_didnuttin; /* Times tcp_output didn't do anything XXX */ +}; + +extern struct tcpstat tcpstat; /* tcp statistics */ +extern uint32_t tcp_now; /* for RFC 1323 timestamps */ + +#endif diff --git a/src/tcpip.h b/src/tcpip.h new file mode 100644 index 0000000..dff5a3c --- /dev/null +++ b/src/tcpip.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcpip.h 8.1 (Berkeley) 6/10/93 + * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp + */ + +#ifndef _TCPIP_H_ +#define _TCPIP_H_ + +/* + * Tcp+ip header, after ip options removed. + */ +struct tcpiphdr { + struct ipovly ti_i; /* overlaid ip structure */ + struct tcphdr ti_t; /* tcp header */ +}; +#define ti_next ti_i.ih_next +#define ti_prev ti_i.ih_prev +#define ti_x1 ti_i.ih_x1 +#define ti_pr ti_i.ih_pr +#define ti_len ti_i.ih_len +#define ti_src ti_i.ih_src +#define ti_dst ti_i.ih_dst +#define ti_sport ti_t.th_sport +#define ti_dport ti_t.th_dport +#define ti_seq ti_t.th_seq +#define ti_ack ti_t.th_ack +#define ti_x2 ti_t.th_x2 +#define ti_off ti_t.th_off +#define ti_flags ti_t.th_flags +#define ti_win ti_t.th_win +#define ti_sum ti_t.th_sum +#define ti_urp ti_t.th_urp + +/* + * Just a clean way to get to the first byte + * of the packet + */ +struct tcpiphdr_2 { + struct tcpiphdr dummy; + char first_char; +}; + +#endif diff --git a/src/tftp.h b/src/tftp.h new file mode 100644 index 0000000..7e06c94 --- /dev/null +++ b/src/tftp.h @@ -0,0 +1 @@ +/* Dummy */ diff --git a/src/udp.c b/src/udp.c new file mode 100644 index 0000000..4373544 --- /dev/null +++ b/src/udp.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94 + * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp + */ +#ifdef _WIN32 +# include +# undef errno +# define errno (WSAGetLastError()) +#else +# include +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "slirp.h" +#include "debug.h" // merge with slirp.h +#include "misc.h" +#include "socket.h" +#include "mbuf.h" +#include "if.h" +#include "ip.h" +#include "ip_icmp.h" +#include "udp.h" +#include "bootp.h" + + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + + +struct udpstat udpstat; +struct SLIRPsocket *udp_last_so = &udb; +struct SLIRPsocket udb; + +/* + * UDP protocol implementation. + * Per RFC 768, August, 1980. + */ +#ifndef COMPAT_42 +int udpcksum = 1; +#else +int udpcksum = 0; /* XXX */ +#endif + + +static const struct tos_t udptos[] = { + { 0, 53, IPTOS_LOWDELAY, 0 }, /* DNS */ + { 517, 517, IPTOS_LOWDELAY, EMU_TALK }, /* talk */ + { 518, 518, IPTOS_LOWDELAY, EMU_NTALK }, /* ntalk */ + { 0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME }, /* Cu-Seeme */ + { 0, 0, 0, 0 } +}; + + +uint8_t +udp_tos(struct SLIRPsocket *so) +{ + int i = 0; + + while (udptos[i].tos) { + if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) || + (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) { + so->so_emu = udptos[i].emu; + return udptos[i].tos; + } + i++; + } + + return 0; +} + + +void +udp_init(void) +{ + udb.so_next = udb.so_prev = &udb; +} + + +/* m->m_data points at ip packet header + * m->m_len length ip packet + * ip->ip_len length data (IPDU) + */ +void +udp_input(struct SLIRPmbuf *m, int iphlen) +{ + struct SLIRPsocket *so, *tmp; +#if 0 + struct SLIRPmbuf *opts = 0; +#endif + struct ip save_ip; + struct udphdr *uh; + struct ip *ip; + int len; + + udpstat.udps_ipackets++; + + /* + * Strip IP options, if any; should skip this, + * make available to user, and use on returned packets, + * but we don't yet have a way to check the checksum + * with options still present. + */ + if (iphlen > sizeof(struct ip)) { + ip_stripoptions(m, (struct SLIRPmbuf *)0); + iphlen = sizeof(struct ip); + } + + /* Get IP and UDP header together in first SLIRPmbuf. */ + ip = mtod(m, struct ip *); + uh = (struct udphdr *)((SLIRPcaddr_t)ip + iphlen); + + /* + * Make SLIRPmbuf data length reflect UDP length. + * If not enough data to reflect UDP length, drop. + */ + len = ntohs((uint16_t)uh->uh_ulen); + if (ip->ip_len != len) { + if (len > ip->ip_len) { + udpstat.udps_badlen++; + goto bad; + } + m_adj(m, len - ip->ip_len); + ip->ip_len = len; + } + + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip = *ip; + save_ip.ip_len+= iphlen; /* tcp_input subtracts this */ + + /* Checksum extended UDP header and data. */ + if (udpcksum && uh->uh_sum) { + ((struct ipovly *)ip)->ih_next = 0; + ((struct ipovly *)ip)->ih_prev = 0; + ((struct ipovly *)ip)->ih_x1 = 0; + ((struct ipovly *)ip)->ih_len = uh->uh_ulen; + + /* keep uh_sum for ICMP reply + * uh->uh_sum = cksum(m, len + sizeof (struct ip)); + * if (uh->uh_sum) { + */ + if (cksum(m, len + sizeof(struct ip))) { + udpstat.udps_badsum++; + goto bad; + } + } + + /* Handle DHCP/BOOTP. */ + if (ntohs(uh->uh_dport) == BOOTP_SERVER) { + bootp_input(m); + goto bad; + } + +#ifdef NEED_TFTP + /* Handle TFTP. */ + if (ntohs(uh->uh_dport) == TFTP_SERVER) { + tftp_input(m); + goto bad; + } +#endif + + /* Locate pcb for datagram. */ + so = udp_last_so; + if (so->so_lport != uh->uh_sport || + so->so_laddr.s_addr != ip->ip_src.s_addr) { + for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) { + if (tmp->so_lport == uh->uh_sport && + tmp->so_laddr.s_addr == ip->ip_src.s_addr) { + tmp->so_faddr.s_addr = ip->ip_dst.s_addr; + tmp->so_fport = uh->uh_dport; + so = tmp; + break; + } + } + + if (tmp != &udb) { + udpstat.udpps_pcbcachemiss++; + udp_last_so = so; + } else + so = NULL; + } + + if (so == NULL) { + /* If there's no socket for this packet, create one. */ + if ((so = socreate()) == NULL) goto bad; + + if (udp_attach(so) == -1) { + DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", + errno,strerror(errno))); + sofree(so); + goto bad; + } + + /* Set up fields. */ +#if 0 + udp_last_so = so; +#endif + so->so_laddr = ip->ip_src; + so->so_lport = uh->uh_sport; + + if ((so->so_iptos = udp_tos(so)) == 0) + so->so_iptos = ip->ip_tos; + + /* + * XXXXX Here, check if it's in udpexec_list, + * and if it is, do the fork_exec() etc. + */ + } + + so->so_faddr = ip->ip_dst; /* XXX */ + so->so_fport = uh->uh_dport; /* XXX */ + + iphlen += sizeof(struct udphdr); + m->m_len -= iphlen; + m->m_data += iphlen; + +#ifdef NEED_EMU + /* Now we sendto() the packet. */ + if (so->so_emu) + udp_emu(so, m); +#endif + + if (sosendto(so,m) == -1) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); + icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + } + + m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ + + /* restore the orig SLIRPmbuf packet */ + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + so->so_m = m; /* ICMP backup */ + + return; +bad: + m_freem(m); +#if 0 + if (opts) m_freem(opts); +#endif +} + + +int +udp_output2(struct SLIRPsocket *so, struct SLIRPmbuf *m, + struct sockaddr_in *saddr, struct sockaddr_in *daddr, int iptos) +{ + struct udpiphdr *ui; + int error = 0; + + /* Adjust for header. */ + m->m_data -= sizeof(struct udpiphdr); + m->m_len += sizeof(struct udpiphdr); + + /* + * Fill in SLIRPmbuf with extended UDP header + * and addresses and length put into network format. + */ + ui = mtod(m, struct udpiphdr *); + ui->ui_next = ui->ui_prev = 0; + ui->ui_x1 = 0; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */ + /* XXXXX Check for from-one-location sockets, or from-any-location sockets */ + ui->ui_src = saddr->sin_addr; + ui->ui_dst = daddr->sin_addr; + ui->ui_sport = saddr->sin_port; + ui->ui_dport = daddr->sin_port; + ui->ui_ulen = ui->ui_len; + + /* Stuff checksum and output datagram. */ + ui->ui_sum = 0; + if (udpcksum) { + if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0) + ui->ui_sum = 0xffff; + } + ((struct ip *)ui)->ip_len = m->m_len; + + ((struct ip *)ui)->ip_ttl = ip_defttl; + ((struct ip *)ui)->ip_tos = iptos; + + udpstat.udps_opackets++; + + error = ip_output(so, m); + + return(error); +} + + +int +udp_output(struct SLIRPsocket *so, struct SLIRPmbuf *m, + struct sockaddr_in *addr) +{ + struct sockaddr_in saddr, daddr; + + saddr = *addr; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + saddr.sin_addr.s_addr = so->so_faddr.s_addr; + if ((so->so_faddr.s_addr & htonl(0x000000ff)) == htonl(0xff)) + saddr.sin_addr.s_addr = router_addr.s_addr; + } + daddr.sin_addr = so->so_laddr; + daddr.sin_port = so->so_lport; + + return udp_output2(so, m, &saddr, &daddr, so->so_iptos); +} + + +int +udp_attach(struct SLIRPsocket *so) +{ + struct sockaddr_in addr; + + if ((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) { + /* + * Here, we bind() the socket. Although not really needed + * (sendto() on an unbound socket will bind it), it's done + * here so that emulation of ytalk etc. don't have to do it + */ + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(so->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + int lasterrno = errno; + closesocket(so->s); + so->s = -1; +#ifdef _WIN32 + WSASetLastError(lasterrno); +#else + errno = lasterrno; +#endif + } else { + /* success, insert in queue */ + so->so_expire = curtime + SO_EXPIRE; + insque(so,&udb); + } + } + + return(so->s); +} + + +void +udp_detach(struct SLIRPsocket *so) +{ + closesocket(so->s); + +#if 0 + if (so->so_m) + /* done by sofree */ + m_free(so->so_m); +#endif + + sofree(so); +} + + +#ifdef USE_REDIR +struct SLIRPsocket * +udp_listen(u_int port, uint32_t laddr, u_int lport, int flags) +{ + socklen_t addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in addr; + struct SLIRPsocket *so; + int opt = 1; + + if ((so = socreate()) == NULL) { + free(so); + return NULL; + } + so->s = socket(AF_INET,SOCK_DGRAM,0); + so->so_expire = curtime + SO_EXPIRE; + insque(so,&udb); + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = port; + + if (bind(so->s, (struct sockaddr *)&addr, addrlen) < 0) { + udp_detach(so); + return NULL; + } + setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)); +#if 0 + setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int)); +#endif + + getsockname(so->s,(struct sockaddr *)&addr,&addrlen); + so->so_fport = addr.sin_port; + if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) + so->so_faddr = alias_addr; + else + so->so_faddr = addr.sin_addr; + + so->so_lport = lport; + so->so_laddr.s_addr = laddr; + if (flags != SS_FACCEPTONCE) + so->so_expire = 0; + + so->so_state = SS_ISFCONNECTED; + + return so; +} +#endif /*USE_REDIR*/ diff --git a/src/udp.h b/src/udp.h new file mode 100644 index 0000000..dca9529 --- /dev/null +++ b/src/udp.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp.h 8.1 (Berkeley) 6/10/93 + * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp + */ +#ifndef _UDP_H_ +#define _UDP_H_ + + +#define UDP_TTL 0x60 +#define UDP_UDPDATALEN 16192 + + +extern struct SLIRPsocket *udp_last_so; + +/* + * Udp protocol header. + * Per RFC 768, September, 1981. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif +struct udphdr { + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + int16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +} PACKED__; +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +/* + * UDP kernel structures and variables. + */ +struct udpiphdr { + struct ipovly ui_i; /* overlaid ip structure */ + struct udphdr ui_u; /* udp header */ +}; +#define ui_next ui_i.ih_next +#define ui_prev ui_i.ih_prev +#define ui_x1 ui_i.ih_x1 +#define ui_pr ui_i.ih_pr +#define ui_len ui_i.ih_len +#define ui_src ui_i.ih_src +#define ui_dst ui_i.ih_dst +#define ui_sport ui_u.uh_sport +#define ui_dport ui_u.uh_dport +#define ui_ulen ui_u.uh_ulen +#define ui_sum ui_u.uh_sum + +struct udpstat { + /* input statistics: */ + u_long udps_ipackets; /* total input packets */ + u_long udps_hdrops; /* packet shorter than header */ + u_long udps_badsum; /* checksum error */ + u_long udps_badlen; /* data length larger than packet */ + u_long udps_noport; /* no socket on port */ + u_long udps_noportbcast; /* of above, arrived as broadcast */ + u_long udps_fullsock; /* not delivered, input socket full */ + u_long udpps_pcbcachemiss; /* input packets missing pcb cache */ + /* output statistics: */ + u_long udps_opackets; /* total output packets */ +}; + +/* + * Names for UDP sysctl objects + */ +#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */ +#define UDPCTL_MAXID 2 + +extern struct udpstat udpstat; +extern struct SLIRPsocket udb; +struct SLIRPmbuf; + + +extern void udp_init(void); +extern void udp_input(register struct SLIRPmbuf *, int); +extern int udp_output(struct SLIRPsocket *, struct SLIRPmbuf *, + struct sockaddr_in *); +extern int udp_attach(struct SLIRPsocket *); +extern void udp_detach(struct SLIRPsocket *); +extern uint8_t udp_tos(struct SLIRPsocket *); +extern void udp_emu(struct SLIRPsocket *, struct SLIRPmbuf *); +extern struct SLIRPsocket * udp_listen(u_int, uint32_t, u_int, int); +extern int udp_output2(struct SLIRPsocket *so, struct SLIRPmbuf *m, + struct sockaddr_in *saddr, + struct sockaddr_in *daddr, int iptos); + + +#endif diff --git a/src/unix/Makefile.linux b/src/unix/Makefile.linux new file mode 100644 index 0000000..45ab3f6 --- /dev/null +++ b/src/unix/Makefile.linux @@ -0,0 +1,271 @@ +# +# VARCem Virtual ARchaeological Computer EMulator. +# An emulator of (mostly) x86-based PC systems and devices, +# using the ISA,EISA,VLB,MCA and PCI system buses, roughly +# spanning the era between 1981 and 1995. +# +# This file is part of the VARCem Project. +# +# Makefile for Debian (Jessie 8.9, 32bit) Linux. +# +# Since many UNIX-ish systems share common stuff, this file is +# also a template for systems similar to Debian Linux and the +# GCC compiler. See the "PLAT" and "PLATDIR" definitions below. +# +# Version: @(#)Makefile.linux 1.0.1 2018/11/02 +# +# Author: Fred N. van Kempen, +# +# Copyright 2017,2018 Fred N. van Kempen. +# +# Redistribution and use in source and binary forms, with +# or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the entire +# above notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names +# of its contributors may be used to endorse or promote +# products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Defaults for several build options (possibly defined in a chained file.) +ifndef AUTODEP +AUTODEP := n +endif +ifndef DEBUG +DEBUG := n +endif +ifndef OPTIM +OPTIM := n +endif +ifndef RELEASE +RELEASE := n +endif +ifndef X64 +X64 := n +endif + + +# Name of the executable. +ifndef PROG + PROG := libslirp +endif +ifeq ($(DEBUG), y) + PROG := $(PROG)-d +endif + + +# Select the desired platform. +PLAT := Linux +PLATDEFS := -DLINUX -DPLAT=\"$(PLAT)\" +PLATNAME := linux +PLATDIR := unix + + +######################################################################### +# Nothing should need changing from here on.. # +######################################################################### +VPATH := $(EXPATH) . unix + + +ifeq ($(X64), y) + CXX := g++ -m64 + CC := gcc -m64 +else + CXX := g++ -m32 + CC := gcc -m32 +endif +PREPROC := cpp -P +STRIP := strip +DEPS = -MMD -MF $*.d -c $< +DEPFILE := $(PLATDIR)/.depends-$(PLATNAME) + +# Set up the correct toolchain flags. +OPTS := -DUNIX $(PLATDEFS) +ifdef BUILD + OPTS += -DBUILD=$(BUILD) +endif +ifdef COMMIT + OPTS += -DCOMMIT=0x$(COMMIT) +endif +ifdef EXFLAGS + OPTS += $(EXFLAGS) +endif +ifdef EXINC + OPTS += -I$(EXINC) +endif +ifeq ($(X64), y) + ifeq ($(OPTIM), y) + DFLAGS := -march=native + else + DFLAGS := + endif +else + ifeq ($(OPTIM), y) + DFLAGS := -march=native + else + DFLAGS := -march=i686 + endif +endif +ifeq ($(DEBUG), y) + DFLAGS += -ggdb -D_DEBUG + AOPTIM := + ifndef COPTIM + COPTIM := -Og + endif +else + ifeq ($(OPTIM), y) + AOPTIM := -mtune=native + ifndef COPTIM + COPTIM := -O3 + endif + else + ifndef COPTIM + COPTIM := -O3 + endif + endif +endif +AFLAGS := -msse2 -mfpmath=sse +LDFLAGS := -pthread +LIBS := + +# Fix up the flags based on selected features. +ifeq ($(RELEASE), y) + OPTS += -DRELEASE_BUILD +endif + + +# Finalize the list of libraries to link in. +LIBS += -ldl -lpthread -lgcc +#ifneq ($(X64), y) +#LIBS += -Wl,--large-address-aware +#endif + + +# Final versions of the toolchain flags. +CFLAGS := $(WX_FLAGS) $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \ + $(AFLAGS) -fomit-frame-pointer -mstackrealign -Wall \ + -Wno-unused-result + +CXXFLAGS := $(WX_FLAGS) $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \ + $(AFLAGS) -fno-strict-aliasing -fvisibility=hidden \ + -Wall -Wundef -fvisibility-inlines-hidden \ + -Wunused-parameter -Wno-ctor-dtor-privacy \ + -Woverloaded-virtual + + +######################################################################### +# Create the (final) list of objects to build. # +######################################################################### + +OBJ := slirp.o host.o misc.o poll.o \ + cksum.o if.o mbuf.o queue.o sbuf.o socket.o \ + debug.o stats.o \ + arp.o bootp.o \ + ip_icmp.o ip_input.o ip_output.o \ + tcp_input.o tcp_output.o tcp_subr.o tcp_timer.o \ + udp.o +ifdef EXOBJ +OBJ += $(EXOBJ) +endif + + +# Build module rules. +ifeq ($(AUTODEP), y) +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $(DEPS) -c $< + +%.o: %.cpp + @echo $< + @$(CXX) $(CXXFLAGS) $(DEPS) -c $< +else +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) -c $< + +%.i: %.c + @echo $< + @$(PREPROC) $(CFLAGS) -P $< + +%.o: %.cpp + @echo $< + @$(CXX) $(CXXFLAGS) -c $< + +%.d: %.c $(wildcard $*.d) + @echo $< + @$(CC) $(CFLAGS) $(DEPS) -E $< >/dev/null + +%.d: %.cpp $(wildcard $*.d) + @echo $< + @$(CXX) $(CXXFLAGS) $(DEPS) -E $< >/dev/null +endif + + +all: $(PREBUILD) $(PROG).so $(POSTBUILD) + + +$(PROG).so: $(OBJ) + @echo Linking $(PROG).so + @$(CXX) $(LDFLAGS) -shared -o $(PROG).so $(OBJ) $(LIBS) +#ifneq ($(DEBUG), y) +# @strip $(PROG).so +#endif + + +clean: + @echo Cleaning objects.. + @-rm -f *.o 2>/dev/null + +clobber: clean + @echo Cleaning executables.. + @-rm -f *.d 2>/dev/null + @-rm -f $(PROG).so 2>/dev/null +# @-rm -f $(DEPFILE) 2>/dev/null + +ifneq ($(AUTODEP), y) +depclean: + @-rm -f $(DEPFILE) 2>/dev/null + @echo Creating dependencies.. + @echo # Run "make depends" to re-create this file. >$(DEPFILE) + +depends: DEPOBJ=$(OBJ:%.o=%.d) +depends: depclean $(OBJ:%.o=%.d) + @-cat $(DEPOBJ) >>$(DEPFILE) + @-rm -f $(DEPOBJ) + +$(DEPFILE): +endif + + +# Module dependencies. +ifeq ($(AUTODEP), y) +#-include $(OBJ:%.o=%.d) (better, but sloooowwwww) +-include *.d +else +include $(wildcard $(DEPFILE)) +endif + + +# End of Makefile.linux. diff --git a/src/unix/host.c b/src/unix/host.c new file mode 100644 index 0000000..b019c16 --- /dev/null +++ b/src/unix/host.c @@ -0,0 +1,81 @@ +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +# include +#endif +#include +#include +#include +#include +#define HAVE_INADDR_H +#include "../slirp.h" +#include "../debug.h" + + +int +host_get_info(struct in_addr *dns, char *hostname, char *domainname) +{ + char buff[512], buff2[256]; + struct in_addr addr; + struct hostent *he; + int found = 0; + FILE *f; + + f = fopen("/etc/resolv.conf", "r"); + if (!f) + return -1; + + /* Set up hostname, domainname and IP address. */ + if (! gethostname(buff, sizeof(buff))) { + he = gethostbyname(buff); + if (he) + our_addr = *(struct in_addr *)he->h_addr; + strcpy(hostname, buff); + strcpy(domainname, ""); + } +strcpy(domainname, "homenet.lan"); + lprint("My name: %s.%s\n", hostname, domainname); + lprint("My IP address: %s\n", inet_ntoa(our_addr)); + + lprint(" DNS Servers:\n" ); + while (fgets(buff, 512, f) != NULL) { + if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { + if (! inet_aton(buff2, &addr)) + continue; + if (addr.s_addr == loopback_addr.s_addr) + addr = our_addr; + + /* If it's the first one, set it to dns_addr */ + if (! found++) + *dns = addr; + + lprint(" Address: %s\n", inet_ntoa(addr)); + } + } + fclose(f); + + if (! found) + return -1; + + return 0; +} + + +int +host_init(void) +{ + /* Nothing to do, usually. */ + + return 0; +} + + +void +host_close(void) +{ + /* Nothing to do. */ +} diff --git a/src/unused/main.h b/src/unused/main.h new file mode 100644 index 0000000..c1c9ef6 --- /dev/null +++ b/src/unused/main.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#define TOWRITEMAX 512 + +/* + * Get the difference in 2 times from updtim() + * Allow for wraparound times, "just in case" + * x is the greater of the 2 (current time) and y is + * what it's being compared against. + */ +#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y) + diff --git a/src/unused/misc.c b/src/unused/misc.c new file mode 100644 index 0000000..99e9fba --- /dev/null +++ b/src/unused/misc.c @@ -0,0 +1,128 @@ +void +snooze(void) +{ + sigset_t s; + int i; + + /* Don't need our data anymore */ + /* XXX This makes SunOS barf */ +/* brk(0); */ + + /* Close all fd's */ + for (i = 255; i >= 0; i--) + close(i); + + signal(SIGQUIT, slirp_exit); + signal(SIGHUP, snooze_hup); + sigemptyset(&s); + + /* Wait for any signal */ + sigsuspend(&s); + + /* Just in case ... */ + exit(255); +} + + +void +relay(int s) +{ + char buf[8192]; + int n; + fd_set readfds; + struct ttys *ttyp; + + /* Don't need our data anymore */ + /* XXX This makes SunOS barf */ +/* brk(0); */ + + signal(SIGQUIT, slirp_exit); + signal(SIGHUP, slirp_exit); + signal(SIGINT, slirp_exit); + signal(SIGTERM, slirp_exit); + + /* Fudge to get term_raw and term_restore to work */ + if (NULL == (ttyp = tty_attach (0, slirp_tty))) { + lprint ("Error: tty_attach failed in misc.c:relay()\r\n"); + slirp_exit (1); + } + ttyp->fd = 0; + ttyp->flags |= TTY_CTTY; + term_raw(ttyp); + + while (1) { + FD_ZERO(&readfds); + + FD_SET(0, &readfds); + FD_SET(s, &readfds); + + n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0); + + if (n <= 0) + slirp_exit(0); + + if (FD_ISSET(0, &readfds)) { + n = read(0, buf, 8192); + if (n <= 0) + slirp_exit(0); + n = writen(s, buf, n); + if (n <= 0) + slirp_exit(0); + } + + if (FD_ISSET(s, &readfds)) { + n = read(s, buf, 8192); + if (n <= 0) + slirp_exit(0); + n = writen(0, buf, n); + if (n <= 0) + slirp_exit(0); + } + } + + /* Just in case.... */ + exit(1); +} + + +#ifdef BAD_SPRINTF +# undef vsprintf +# undef sprintf + +/* Some BSD-derived systems have a sprintf which returns char *. */ +int +vsprintf_len(char *string, const char *format, va_list args) +{ + vsprintf(string, format, args); + + return strlen(string); +} + + +int +sprintf_len(char *string, const char *format, ...) +{ + va_list args; + + va_start(args, format); + + vsprintf(string, format, args); + + return strlen(string); +} +#endif + + +void +u_sleep(int usec) +{ + struct timeval t; + fd_set fdset; + + FD_ZERO(&fdset); + + t.tv_sec = 0; + t.tv_usec = usec * 1000; + + select(0, &fdset, &fdset, &fdset, &t); +} diff --git a/src/unused/redir.c b/src/unused/redir.c new file mode 100644 index 0000000..dbede21 --- /dev/null +++ b/src/unused/redir.c @@ -0,0 +1,65 @@ +#include "slirp.h" +#ifdef USE_REDIR +#include "redir.h" + + +struct ex_list *exec_list = NULL; + + +int +redir_init(void) +{ + const char *cat = "SLiRP Port Forwarding"; + char temp[256]; + int rc, udp, from, to; + int i = 0; + + for (;;) { + sprintf(temp, "%d_udp", i); + udp = config_get_int(cat, temp, 0); + sprintf(temp, "%d_from", i); + from = config_get_int(cat, temp, 0); + + if (from < 1) + break; + sprintf(temp, "%d_to", i); + to = config_get_int(cat, temp, from); + + rc = slirp_redir(udp, from, myaddr, to); + if (rc == 0) + pclog(1, "slirp redir %d -> %d successful\n", from, to); + else + pclog(1, "slirp redir %d -> %d failed (%d)\n", from, to, rc); + + i++; + } +} + + +int +slirp_redir(int is_udp, int host_port, + struct in_addr guest_addr, int guest_port) +{ + if (is_udp) { + if (! udp_listen(htons(host_port), guest_addr.s_addr, + htons(guest_port), 0)) + return -1; + } else { + if (! solisten(htons(host_port), guest_addr.s_addr, + htons(guest_port), 0)) + return -1; + } + + return 0; +} + + +int +slirp_add_exec(int do_pty, const char *args, int addr_low_byte, int guest_port) +{ + return add_exec(&exec_list, do_pty, (char *)args, + addr_low_byte, htons(guest_port)); +} + + +#endif /*USE_REDIR*/ diff --git a/src/unused/redir.h b/src/unused/redir.h new file mode 100644 index 0000000..6274c10 --- /dev/null +++ b/src/unused/redir.h @@ -0,0 +1,12 @@ +#ifndef SLIRP_REDIR_H +# define SLIRP_REDIR_H + + +extern int redir_init(void); +extern int slirp_redir(int is_udp, int host_port, + struct in_addr guest_addr, int guest_port); +extern int slirp_add_exec(int do_pty, const char *args, + int addr_low_byte, int guest_port); + + +#endif /*SLIRP_REDIR_H*/ diff --git a/src/unused/tcp_emu.c b/src/unused/tcp_emu.c new file mode 100644 index 0000000..05f5f94 --- /dev/null +++ b/src/unused/tcp_emu.c @@ -0,0 +1,654 @@ +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#define WANT_SYS_IOCTL_H +#include +#ifndef _WIN32 +# include +#endif +#include "slirp.h" + + +#ifdef USE_REDIR +struct emu_t *tcpemu = 0; +int do_echo = -1; + + +/* + * Emulate programs that try and connect to us + * This includes ftp (the data connection is + * initiated by the server) and IRC (DCC CHAT and + * DCC SEND) for now + * + * NOTE: It's possible to crash SLiRP by sending it + * unstandard strings to emulate... if this is a problem, + * more checks are needed here + * + * XXX Assumes the whole command came in one packet + * + * XXX Some ftp clients will have their TOS set to + * LOWDELAY and so Nagel will kick in. Because of this, + * we'll get the first letter, followed by the rest, so + * we simply scan for ORT instead of PORT... + * DCC doesn't have this problem because there's other stuff + * in the packet before the DCC command. + * + * Return 1 if the SLIRPmbuf m is still valid and should be + * sbappend()ed + * + * NOTE: if you return 0 you MUST m_free() the SLIRPmbuf! + */ +int +tcp_emu(struct SLIRPsocket *so, struct SLIRPmbuf *m) +{ + u_int n1, n2, n3, n4, n5, n6; + char buff[256]; + u_int32_t laddr; + u_int lport; + char *bptr; + + DEBUG_CALL("tcp_emu"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + + switch(so->so_emu) { + int x, i; + + case EMU_IDENT: + /* + * Identification protocol as per rfc-1413 + */ + + { + struct SLIRPsocket *tmpso; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + struct sbuf *so_rcv = &so->so_rcv; + + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; + m->m_data[m->m_len] = 0; /* NULL terminate */ + if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) { + if (sscanf(so_rcv->sb_data, "%d%*[ ,]%d", &n1, &n2) == 2) { + HTONS(n1); + HTONS(n2); + /* n2 is the one on our host */ + for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) { + if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr && + tmpso->so_lport == n2 && + tmpso->so_faddr.s_addr == so->so_faddr.s_addr && + tmpso->so_fport == n1) { + if (getsockname(tmpso->s, + (struct sockaddr *)&addr, &addrlen) == 0) + n2 = ntohs(addr.sin_port); + break; + } + } + } + so_rcv->sb_cc = sprintf(so_rcv->sb_data, "%d,%d\r\n", n1, n2); + so_rcv->sb_rptr = so_rcv->sb_data; + so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; + } + m_free(m); + return 0; + } + +#if 0 + case EMU_RLOGIN: + /* + * Rlogin emulation + * First we accumulate all the initial option negotiation, + * then fork_exec() rlogin according to the options + */ + { + int i, i2, n; + char *ptr; + char args[100]; + char term[100]; + struct sbuf *so_snd = &so->so_snd; + struct sbuf *so_rcv = &so->so_rcv; + + /* First check if they have a priveladged port, or too much data has arrived */ + if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 || + (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) { + memcpy(so_snd->sb_wptr, "Permission denied\n", 18); + so_snd->sb_wptr += 18; + so_snd->sb_cc += 18; + tcp_sockclosed(sototcpcb(so)); + m_free(m); + return 0; + } + + /* Append the current data */ + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; + m_free(m); + + /* + * Check if we have all the initial options, + * and build argument list to rlogin while we're here + */ + n = 0; + ptr = so_rcv->sb_data; + args[0] = 0; + term[0] = 0; + while (ptr < so_rcv->sb_wptr) { + if (*ptr++ == 0) { + n++; + if (n == 2) { + sprintf(args, "rlogin -l %s %s", + ptr, inet_ntoa(so->so_faddr)); + } else if (n == 3) { + i2 = so_rcv->sb_wptr - ptr; + for (i = 0; i < i2; i++) { + if (ptr[i] == '/') { + ptr[i] = 0; +#ifdef HAVE_SETENV + sprintf(term, "%s", ptr); +#else + sprintf(term, "TERM=%s", ptr); +#endif + ptr[i] = '/'; + break; + } + } + } + } + } + + if (n != 4) + return 0; + + /* We have it, set our term variable and fork_exec() */ +#ifdef HAVE_SETENV + setenv("TERM", term, 1); +#else + putenv(term); +#endif + fork_exec(so, args, 2); + term[0] = 0; + so->so_emu = 0; + + /* And finally, send the client a 0 character */ + so_snd->sb_wptr[0] = 0; + so_snd->sb_wptr++; + so_snd->sb_cc++; + + return 0; + } + + case EMU_RSH: + /* + * rsh emulation + * First we accumulate all the initial option negotiation, + * then rsh_exec() rsh according to the options + */ + { + int n; + char *ptr; + char *user; + char *args; + struct sbuf *so_snd = &so->so_snd; + struct sbuf *so_rcv = &so->so_rcv; + + /* First check if they have a priveladged port, or too much data has arrived */ + if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 || + (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) { + memcpy(so_snd->sb_wptr, "Permission denied\n", 18); + so_snd->sb_wptr += 18; + so_snd->sb_cc += 18; + tcp_sockclosed(sototcpcb(so)); + m_free(m); + return 0; + } + + /* Append the current data */ + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; + m_free(m); + + /* + * Check if we have all the initial options, + * and build argument list to rlogin while we're here + */ + n = 0; + ptr = so_rcv->sb_data; + user=""; + args=""; + if (so->extra==NULL) { + struct SLIRPsocket *ns; + struct tcpcb* tp; + int port=atoi(ptr); + if (port <= 0) return 0; + if (port > 1023 || port < 512) { + memcpy(so_snd->sb_wptr, "Permission denied\n", 18); + so_snd->sb_wptr += 18; + so_snd->sb_cc += 18; + tcp_sockclosed(sototcpcb(so)); + return 0; + } + if ((ns=socreate()) == NULL) + return 0; + if (tcp_attach(ns)<0) { + free(ns); + return 0; + } + + ns->so_laddr=so->so_laddr; + ns->so_lport=htons(port); + + (void) tcp_mss(sototcpcb(ns), 0); + + ns->so_faddr=so->so_faddr; + ns->so_fport=htons(IPPORT_RESERVED-1); /* Use a fake port. */ + + if (ns->so_faddr.s_addr == 0 || + ns->so_faddr.s_addr == loopback_addr.s_addr) + ns->so_faddr = alias_addr; + + ns->so_iptos = tcp_tos(ns); + tp = sototcpcb(ns); + + tcp_template(tp); + + /* Compute window scaling to request. */ + /* while (tp->request_r_scale < TCP_MAX_WINSHIFT && + * (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) + * tp->request_r_scale++; + */ + + /*soisfconnecting(ns);*/ + + tcpstat.tcps_connattempt++; + + tp->t_state = TCPS_SYN_SENT; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->iss = tcp_iss; + tcp_iss += TCP_ISSINCR/2; + tcp_sendseqinit(tp); + tcp_output(tp); + so->extra=ns; + } + while (ptr < so_rcv->sb_wptr) { + if (*ptr++ == 0) { + n++; + if (n == 2) { + user=ptr; + } else if (n == 3) { + args=ptr; + } + } + } + + if (n != 4) + return 0; + + rsh_exec(so,so->extra, user, inet_ntoa(so->so_faddr), args); + so->so_emu = 0; + so->extra=NULL; + + /* And finally, send the client a 0 character */ + so_snd->sb_wptr[0] = 0; + so_snd->sb_wptr++; + so_snd->sb_cc++; + + return 0; + } + + case EMU_CTL: + { + int num; + struct sbuf *so_snd = &so->so_snd; + struct sbuf *so_rcv = &so->so_rcv; + + /* + * If there is binary data here, we save it in so->so_m + */ + if (!so->so_m) { + int rxlen; + char *rxdata; + rxdata=mtod(m, char *); + for (rxlen=m->m_len; rxlen; rxlen--) { + if (*rxdata++ & 0x80) { + so->so_m = m; + return 0; + } + } + } /* if(so->so_m==NULL) */ + + /* + * Append the line + */ + sbappendsb(so_rcv, m); + + /* To avoid going over the edge of the buffer, we reset it */ + if (so_snd->sb_cc == 0) + so_snd->sb_wptr = so_snd->sb_rptr = so_snd->sb_data; + + /* + * A bit of a hack: + * If the first packet we get here is 1 byte long, then it + * was done in telnet character mode, therefore we must echo + * the characters as they come. Otherwise, we echo nothing, + * because in linemode, the line is already echoed + * XXX two or more control connections won't work + */ + if (do_echo == -1) { + if (m->m_len == 1) do_echo = 1; + else do_echo = 0; + } + if (do_echo) { + sbappendsb(so_snd, m); + m_free(m); + tcp_output(sototcpcb(so)); /* XXX */ + } else + m_free(m); + + num = 0; + while (num < so->so_rcv.sb_cc) { + if (*(so->so_rcv.sb_rptr + num) == '\n' || + *(so->so_rcv.sb_rptr + num) == '\r') { + int n; + + *(so_rcv->sb_rptr + num) = 0; + if (ctl_password && !ctl_password_ok) { + /* Need a password */ + if (sscanf(so_rcv->sb_rptr, "pass %256s", buff) == 1) { + if (strcmp(buff, ctl_password) == 0) { + ctl_password_ok = 1; + n = sprintf(so_snd->sb_wptr, + "Password OK.\r\n"); + goto do_prompt; + } + } + n = sprintf(so_snd->sb_wptr, + "Error: Password required, log on with \"pass PASSWORD\"\r\n"); + goto do_prompt; + } + cfg_quitting = 0; + n = do_config(so_rcv->sb_rptr, so, PRN_SPRINTF); + if (!cfg_quitting) { + /* Register the printed data */ +do_prompt: + so_snd->sb_cc += n; + so_snd->sb_wptr += n; + /* Add prompt */ + n = sprintf(so_snd->sb_wptr, "Slirp> "); + so_snd->sb_cc += n; + so_snd->sb_wptr += n; + } + /* Drop so_rcv data */ + so_rcv->sb_cc = 0; + so_rcv->sb_wptr = so_rcv->sb_rptr = so_rcv->sb_data; + tcp_output(sototcpcb(so)); /* Send the reply */ + } + num++; + } + return 0; + } +#endif + case EMU_FTP: /* ftp */ + *(m->m_data+m->m_len) = 0; /* NULL terminate for strstr */ + if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) { + /* + * Need to emulate the PORT command + */ + x = sscanf(bptr, "ORT %d,%d,%d,%d,%d,%d\r\n%256[^\177]", + &n1, &n2, &n3, &n4, &n5, &n6, buff); + if (x < 6) + return 1; + + laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); + lport = htons((n5 << 8) | (n6)); + + if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL) + return 1; + + n6 = ntohs(so->so_fport); + + n5 = (n6 >> 8) & 0xff; + n6 &= 0xff; + + laddr = ntohl(so->so_faddr.s_addr); + + n1 = ((laddr >> 24) & 0xff); + n2 = ((laddr >> 16) & 0xff); + n3 = ((laddr >> 8) & 0xff); + n4 = (laddr & 0xff); + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr,"ORT %d,%d,%d,%d,%d,%d\r\n%s", + n1, n2, n3, n4, n5, n6, x==7?buff:""); + return 1; + } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) { + /* + * Need to emulate the PASV response + */ + x = sscanf(bptr, "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%256[^\177]", + &n1, &n2, &n3, &n4, &n5, &n6, buff); + if (x < 6) + return 1; + + laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); + lport = htons((n5 << 8) | (n6)); + + if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL) + return 1; + + n6 = ntohs(so->so_fport); + + n5 = (n6 >> 8) & 0xff; + n6 &= 0xff; + + laddr = ntohl(so->so_faddr.s_addr); + + n1 = ((laddr >> 24) & 0xff); + n2 = ((laddr >> 16) & 0xff); + n3 = ((laddr >> 8) & 0xff); + n4 = (laddr & 0xff); + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr,"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", + n1, n2, n3, n4, n5, n6, x==7?buff:""); + + return 1; + } + + return 1; + + case EMU_KSH: + /* + * The kshell (Kerberos rsh) and shell services both pass + * a local port port number to carry signals to the server + * and stderr to the client. It is passed at the beginning + * of the connection as a NUL-terminated decimal ASCII string. + */ + so->so_emu = 0; + for (lport = 0, i = 0; i < m->m_len-1; ++i) { + if (m->m_data[i] < '0' || m->m_data[i] > '9') + return 1; /* invalid number */ + lport *= 10; + lport += m->m_data[i] - '0'; + } + if (m->m_data[m->m_len-1] == '\0' && lport != 0 && + (so = solisten(0, so->so_laddr.s_addr, htons(lport), SS_FACCEPTONCE)) != NULL) + m->m_len = sprintf(m->m_data, "%d", ntohs(so->so_fport))+1; + return 1; + + case EMU_IRC: + /* + * Need to emulate DCC CHAT, DCC SEND and DCC MOVE + */ + *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */ + if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL) + return 1; + + /* The %256s is for the broken mIRC */ + if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) { + if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL) + return 1; + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr, "DCC CHAT chat %lu %u%c\n", + (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), 1); + } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) { + if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL) + return 1; + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr, "DCC SEND %s %lu %u %u%c\n", + buff, (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), n1, 1); + } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) { + if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL) + return 1; + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr, "DCC MOVE %s %lu %u %u%c\n", + buff, (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), n1, 1); + } + return 1; + + case EMU_REALAUDIO: + /* + * RealAudio emulation - JP. We must try to parse the incoming + * data and try to find the two characters that contain the + * port number. Then we redirect an udp port and replace the + * number with the real port we got. + * + * The 1.0 beta versions of the player are not supported + * any more. + * + * A typical packet for player version 1.0 (release version): + * + * 0000:50 4E 41 00 05 + * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 .....×..gælÜc..P + * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH + * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v + * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB + * + * Now the port number 0x1BD7 is found at offset 0x04 of the + * Now the port number 0x1BD7 is found at offset 0x04 of the + * second packet. This time we received five bytes first and + * then the rest. You never know how many bytes you get. + * + * A typical packet for player version 2.0 (beta): + * + * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA...........Á. + * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .guxõc..Win2.0.0 + * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/ + * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas + * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B + * + * Port number 0x1BC1 is found at offset 0x0d. + * + * This is just a horrible switch statement. Variable ra tells + * us where we're going. + */ + + bptr = m->m_data; + while (bptr < m->m_data + m->m_len) { + u_short p; + static int ra = 0; + char ra_tbl[4]; + + ra_tbl[0] = 0x50; + ra_tbl[1] = 0x4e; + ra_tbl[2] = 0x41; + ra_tbl[3] = 0; + + switch (ra) { + case 0: + case 2: + case 3: + if (*bptr++ != ra_tbl[ra]) { + ra = 0; + continue; + } + break; + + case 1: + /* + * We may get 0x50 several times, ignore them + */ + if (*bptr == 0x50) { + ra = 1; + bptr++; + continue; + } else if (*bptr++ != ra_tbl[ra]) { + ra = 0; + continue; + } + break; + + case 4: + /* + * skip version number + */ + bptr++; + break; + + case 5: + /* + * The difference between versions 1.0 and + * 2.0 is here. For future versions of + * the player this may need to be modified. + */ + if (*(bptr + 1) == 0x02) + bptr += 8; + else + bptr += 4; + break; + + case 6: + /* This is the field containing the port + * number that RA-player is listening to. + */ + lport = (((u_char*)bptr)[0] << 8) + + ((u_char *)bptr)[1]; + if (lport < 6970) + lport += 256; /* don't know why */ + if (lport < 6970 || lport > 7170) + return 1; /* failed */ + + /* try to get udp port between 6970 - 7170 */ + for (p = 6970; p < 7071; p++) { + if (udp_listen( htons(p), + so->so_laddr.s_addr, + htons(lport), + SS_FACCEPTONCE)) { + break; + } + } + if (p == 7071) + p = 0; + *(u_char *)bptr++ = (p >> 8) & 0xff; + *(u_char *)bptr++ = p & 0xff; + ra = 0; + return 1; /* port redirected, we're done */ + break; + + default: + ra = 0; + } + ra++; + } + return 1; + + default: + /* Ooops, not emulated, won't call tcp_emu again */ + so->so_emu = 0; + return 1; + } +} +#endif /*USE_REDIR*/ diff --git a/src/unused/tftp.h b/src/unused/tftp.h new file mode 100644 index 0000000..ba41741 --- /dev/null +++ b/src/unused/tftp.h @@ -0,0 +1,40 @@ +/* tftp defines */ + +#define TFTP_SESSIONS_MAX 3 + +#define TFTP_SERVER 69 + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +#define TFTP_FILENAME_MAX 512 + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct tftp_t { + struct ip ip; + struct udphdr udp; + u_int16_t tp_op; + union { + struct { + u_int16_t tp_block_nr; + u_int8_t tp_buf[512]; + } tp_data; + struct { + u_int16_t tp_error_code; + u_int8_t tp_msg[512]; + } tp_error; + u_int8_t tp_buf[512 + 2]; + } x; +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +void tftp_input(struct SLIRPmbuf *m); diff --git a/src/unused/udp_emu.c b/src/unused/udp_emu.c new file mode 100644 index 0000000..4fd1b5d --- /dev/null +++ b/src/unused/udp_emu.c @@ -0,0 +1,251 @@ +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ +#include +#ifndef _WIN32 +# include +#endif +#include "slirp.h" +#include "ip_icmp.h" +#ifdef EMULATE_TALK +# include "talkd.h" +#endif + + +#ifdef USE_REDIR + + +struct cu_header { + uint16_t d_family; // destination family + uint16_t d_port; // destination port + uint32_t d_addr; // destination address + uint16_t s_family; // source family + uint16_t s_port; // source port + uint32_t so_addr; // source address + uint32_t seqn; // sequence number + uint16_t message; // message + uint16_t data_type; // data type + uint16_t pkt_len; // packet length +}; + +#ifdef EMULATE_TALK +# define IS_OLD (so->so_emu == EMU_TALK) +# define COPY_MSG(dest, src) { \ + dest->type = src->type; \ + dest->id_num = src->id_num; \ + dest->pid = src->pid; \ + dest->addr = src->addr; \ + dest->ctl_addr = src->ctl_addr; \ + memcpy(&dest->l_name, &src->l_name, NAME_SIZE_OLD); \ + memcpy(&dest->r_name, &src->r_name, NAME_SIZE_OLD); \ + memcpy(&dest->r_tty, &src->r_tty, TTY_SIZE); \ + } + +# define OTOSIN(ptr, field) ((struct sockaddr_in *)&ptr->field) + +struct talk_request { + struct talk_request *next; + struct SLIRPsocket *udp_so; + struct SLIRPsocket *tcp_so; +} *req; + + +static struct talk_request *req_tbl = 0; +#endif + + +/* Here, talk/ytalk/ntalk requests must be emulated. */ +void +udp_emu(struct SLIRPsocket *so, struct SLIRPmbuf *m) +{ + socklen_t addrlen = sizeof(addr); + struct sockaddr_in addr; +#ifdef EMULATE_TALK + CTL_MSG_OLD *omsg; + CTL_MSG *nmsg; + char buff[sizeof(CTL_MSG)]; + u_char type; +#endif + struct cu_header *cu_head; + + switch(so->so_emu) { +#ifdef EMULATE_TALK + case EMU_TALK: + case EMU_NTALK: + /* + * Talk emulation. We always change the ctl_addr to get + * some answers from the daemon. When an ANNOUNCE comes, + * we send LEAVE_INVITE to the local daemons. Also when a + * DELETE comes, we send copies to the local daemons. + */ + if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) + return; + + /* old_sockaddr to sockaddr_in */ + if (IS_OLD) { /* old talk */ + omsg = mtod(m, CTL_MSG_OLD*); + nmsg = (CTL_MSG *) buff; + type = omsg->type; + OTOSIN(omsg, ctl_addr)->sin_port = addr.sin_port; + OTOSIN(omsg, ctl_addr)->sin_addr = our_addr; + strncpy(omsg->l_name, getlogin(), NAME_SIZE_OLD); + } else { /* new talk */ + omsg = (CTL_MSG_OLD *) buff; + nmsg = mtod(m, CTL_MSG *); + type = nmsg->type; + OTOSIN(nmsg, ctl_addr)->sin_port = addr.sin_port; + OTOSIN(nmsg, ctl_addr)->sin_addr = our_addr; + strncpy(nmsg->l_name, getlogin(), NAME_SIZE_OLD); + } + + if (type == LOOK_UP) + return; /* for LOOK_UP this is enough */ + + if (IS_OLD) { /* make a copy of the message */ + COPY_MSG(nmsg, omsg); + nmsg->vers = 1; + nmsg->answer = 0; + } else + COPY_MSG(omsg, nmsg); + + /* + * If if is an ANNOUNCE message, we go through the + * request table to see if a tcp port has already + * been redirected for this socket. If not, we solisten() + * a new socket and add this entry to the table. + * The port number of the tcp socket and our IP + * are put to the addr field of the message structures. + * Then a LEAVE_INVITE is sent to both local daemon + * ports, 517 and 518. This is why we have two copies + * of the message, one in old talk and one in new talk + * format. + */ + if (type == ANNOUNCE) { + int s; + u_short temp_port; + + for (req = req_tbl; req; req = req->next) + if (so == req->udp_so) + break; /* found it */ + + if (!req) { /* no entry for so, create new */ + req = (struct talk_request *) + malloc(sizeof(struct talk_request)); + req->udp_so = so; + req->tcp_so = solisten(0, + OTOSIN(omsg, addr)->sin_addr.s_addr, + OTOSIN(omsg, addr)->sin_port, + SS_FACCEPTONCE); + req->next = req_tbl; + req_tbl = req; + } + + /* replace port number in addr field */ + addrlen = sizeof(addr); + getsockname(req->tcp_so->s, + (struct sockaddr *) &addr, + &addrlen); + OTOSIN(omsg, addr)->sin_port = addr.sin_port; + OTOSIN(omsg, addr)->sin_addr = our_addr; + OTOSIN(nmsg, addr)->sin_port = addr.sin_port; + OTOSIN(nmsg, addr)->sin_addr = our_addr; + + /* send LEAVE_INVITEs */ + temp_port = OTOSIN(omsg, ctl_addr)->sin_port; + OTOSIN(omsg, ctl_addr)->sin_port = 0; + OTOSIN(nmsg, ctl_addr)->sin_port = 0; + omsg->type = nmsg->type = LEAVE_INVITE; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + addr.sin_addr = our_addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(517); + sendto(s, (char *)omsg, sizeof(*omsg), 0, + (struct sockaddr *)&addr, sizeof(addr)); + addr.sin_port = htons(518); + sendto(s, (char *)nmsg, sizeof(*nmsg), 0, + (struct sockaddr *) &addr, sizeof(addr)); + closesocket(s) ; + + omsg->type = nmsg->type = ANNOUNCE; + OTOSIN(omsg, ctl_addr)->sin_port = temp_port; + OTOSIN(nmsg, ctl_addr)->sin_port = temp_port; + } + + /* + * If it is a DELETE message, we send a copy to the + * local daemons. Then we delete the entry corresponding + * to our socket from the request table. + */ + if (type == DELETE) { + struct talk_request *temp_req, *req_next; + int s; + u_short temp_port; + + temp_port = OTOSIN(omsg, ctl_addr)->sin_port; + OTOSIN(omsg, ctl_addr)->sin_port = 0; + OTOSIN(nmsg, ctl_addr)->sin_port = 0; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + addr.sin_addr = our_addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(517); + sendto(s, (char *)omsg, sizeof(*omsg), 0, + (struct sockaddr *)&addr, sizeof(addr)); + addr.sin_port = htons(518); + sendto(s, (char *)nmsg, sizeof(*nmsg), 0, + (struct sockaddr *)&addr, sizeof(addr)); + closesocket(s); + + OTOSIN(omsg, ctl_addr)->sin_port = temp_port; + OTOSIN(nmsg, ctl_addr)->sin_port = temp_port; + + /* delete table entry */ + if (so == req_tbl->udp_so) { + temp_req = req_tbl; + req_tbl = req_tbl->next; + free(temp_req); + } else { + temp_req = req_tbl; + for(req = req_tbl->next; req; req = req_next) { + req_next = req->next; + if (so == req->udp_so) { + temp_req->next = req_next; + free(req); + break; + } else { + temp_req = req; + } + } + } + } + + return; +#endif + + case EMU_CUSEEME: + + /* + * Cu-SeeMe emulation. + * Hopefully the packet is more that 16 bytes long. We don't + * do any other tests, just replace the address and port + * fields. + */ + if (m->m_len >= sizeof (*cu_head)) { + if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) + return; + cu_head = mtod(m, struct cu_header *); + cu_head->s_port = addr.sin_port; + cu_head->so_addr = our_addr.s_addr; + } + + return; + } +} + + +#endif /*USE_REDIR*/ diff --git a/src/unused/udp_tftp.c b/src/unused/udp_tftp.c new file mode 100644 index 0000000..b7b1ffb --- /dev/null +++ b/src/unused/udp_tftp.c @@ -0,0 +1,332 @@ +/* + * tftp.c - a simple, read-only tftp server for qemu + * + * Copyright (c) 2004 Magnus Damm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "slirp.h" +#include "../ibm.h" + +struct tftp_session { + int in_use; + char filename[TFTP_FILENAME_MAX]; + + struct in_addr client_ip; + u_int16_t client_port; + + int timestamp; +}; + +struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + +const char *tftp_prefix; + +static void tftp_session_update(struct tftp_session *spt) +{ + spt->timestamp = curtime; + spt->in_use = 1; +} + +static void tftp_session_terminate(struct tftp_session *spt) +{ + spt->in_use = 0; +} + +static int tftp_session_allocate(struct tftp_t *tp) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &tftp_sessions[k]; + + if (!spt->in_use) + goto found; + + /* sessions time out after 5 inactive seconds */ + if ((int)(curtime - spt->timestamp) > 5000) + goto found; + } + + return -1; + + found: + memset(spt, 0, sizeof(*spt)); + memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)); + spt->client_port = tp->udp.uh_sport; + + tftp_session_update(spt); + + return k; +} + +static int tftp_session_find(struct tftp_t *tp) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &tftp_sessions[k]; + + if (spt->in_use) { + if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) { + if (spt->client_port == tp->udp.uh_sport) { + return k; + } + } + } + } + + return -1; +} + +static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr, + u_int8_t *buf, int len) +{ + int fd; + int bytes_read = 0; + + char file_path[sizeof(pcempath) + 5 + sizeof(spt->filename)]; + strcpy(file_path, pcempath); + strcat(file_path, "tftp/"); + strcat(file_path, spt->filename); + + fd = open(file_path, O_RDONLY | O_BINARY); + + if (fd < 0) { + return -1; + } + + if (len) { + lseek(fd, block_nr * 512, SEEK_SET); + + bytes_read = read(fd, buf, len); + } + + close(fd); + + return bytes_read; +} + +static int tftp_send_error(struct tftp_session *spt, + u_int16_t errorcode, const char *msg, + struct tftp_t *recv_tp) +{ + struct sockaddr_in saddr, daddr; + struct SLIRPmbuf *m; + struct tftp_t *tp; + int nobytes; + + m = m_get(); + + if (!m) { + return -1; + } + + memset(m->m_data, 0, m->m_size); + + m->m_data += if_maxlinkhdr; + tp = (void *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + + tp->tp_op = htons(TFTP_ERROR); + tp->x.tp_error.tp_error_code = htons(errorcode); + strncpy((char *)tp->x.tp_error.tp_msg, msg, sizeof(tp->x.tp_error.tp_msg)); + tp->x.tp_error.tp_msg[sizeof(tp->x.tp_error.tp_msg)-1] = 0; + + saddr.sin_addr = recv_tp->ip.ip_dst; + saddr.sin_port = recv_tp->udp.uh_dport; + + daddr.sin_addr = spt->client_ip; + daddr.sin_port = spt->client_port; + + nobytes = 2; + + m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - + sizeof(struct ip) - sizeof(struct udphdr); + + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + + tftp_session_terminate(spt); + + return 0; +} + +static int tftp_send_data(struct tftp_session *spt, + u_int16_t block_nr, + struct tftp_t *recv_tp) +{ + struct sockaddr_in saddr, daddr; + struct SLIRPmbuf *m; + struct tftp_t *tp; + int nobytes; + + if (block_nr < 1) { + return -1; + } + + m = m_get(); + + if (!m) { + return -1; + } + + memset(m->m_data, 0, m->m_size); + + m->m_data += if_maxlinkhdr; + tp = (void *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + + tp->tp_op = htons(TFTP_DATA); + tp->x.tp_data.tp_block_nr = htons(block_nr); + + saddr.sin_addr = recv_tp->ip.ip_dst; + saddr.sin_port = recv_tp->udp.uh_dport; + + daddr.sin_addr = spt->client_ip; + daddr.sin_port = spt->client_port; + + nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512); + + if (nobytes < 0) { + m_free(m); + + /* send "file not found" error back */ + + tftp_send_error(spt, 1, "File not found", tp); + + return -1; + } + + m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - + sizeof(struct ip) - sizeof(struct udphdr); + + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + + if (nobytes == 512) { + tftp_session_update(spt); + } + else { + tftp_session_terminate(spt); + } + + return 0; +} + +static void tftp_handle_rrq(struct tftp_t *tp, int pktlen) +{ + struct tftp_session *spt; + int s, k, n; + u_int8_t *src, *dst; + + s = tftp_session_allocate(tp); + + if (s < 0) { + return; + } + + spt = &tftp_sessions[s]; + + src = tp->x.tp_buf; + dst = (u_int8_t *)spt->filename; + n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp); + + /* get name */ + + for (k = 0; k < n; k++) { + if (k < TFTP_FILENAME_MAX) { + dst[k] = src[k]; + } + else { + return; + } + + if (src[k] == '\0') { + break; + } + } + + if (k >= n) { + return; + } + + k++; + + /* check mode */ + if ((n - k) < 6) { + return; + } + + if (memcmp(&src[k], "octet\0", 6) != 0) { + tftp_send_error(spt, 4, "Unsupported transfer mode", tp); + return; + } + + pclog("tftp request: %s\n", spt->filename); + + /* do sanity checks on the filename */ + + if (strstr(spt->filename, "../") || strstr(spt->filename, "..\\")) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + /* check if the file exists */ + + if (tftp_read_data(spt, 0, (u_int8_t *)spt->filename, 0) < 0) { + tftp_send_error(spt, 1, "File not found", tp); + return; + } + + tftp_send_data(spt, 1, tp); +} + +static void tftp_handle_ack(struct tftp_t *tp, int pktlen) +{ + int s; + + s = tftp_session_find(tp); + + if (s < 0) { + return; + } + + if (tftp_send_data(&tftp_sessions[s], + ntohs(tp->x.tp_data.tp_block_nr) + 1, + tp) < 0) { + return; + } +} + +void tftp_input(struct SLIRPmbuf *m) +{ + struct tftp_t *tp = (struct tftp_t *)m->m_data; + + switch(ntohs(tp->tp_op)) { + case TFTP_RRQ: + tftp_handle_rrq(tp, m->m_len); + break; + + case TFTP_ACK: + tftp_handle_ack(tp, m->m_len); + break; + } +} diff --git a/src/unused/x b/src/unused/x new file mode 100644 index 0000000..2daf34d --- /dev/null +++ b/src/unused/x @@ -0,0 +1,653 @@ +int +add_exec(struct ex_list **ex_ptr, int do_pty, char *exec, int addr, int port) +{ + struct ex_list *tmp_ptr; + + /* First, check if the port is "bound" */ + for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) { + if (port == tmp_ptr->ex_fport && addr == tmp_ptr->ex_addr) + return -1; + } + + tmp_ptr = *ex_ptr; + *ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list)); + (*ex_ptr)->ex_fport = port; + (*ex_ptr)->ex_addr = addr; + (*ex_ptr)->ex_pty = do_pty; + (*ex_ptr)->ex_exec = strdup(exec); + (*ex_ptr)->ex_next = tmp_ptr; + return 0; +} + + +#ifdef _WIN32 +int +fork_exec(struct SLIRPsocket *so, char *ex, int do_pty) +{ + /* not implemented */ + return 0; +} +#else +int +slirp_openpty(int *amaster, int *aslave) +{ + register int master, slave; + +#ifdef HAVE_GRANTPT + char *ptr; + + if ((master = open("/dev/ptmx", O_RDWR)) < 0 || + grantpt(master) < 0 || + unlockpt(master) < 0 || + (ptr = ptsname(master)) == NULL) { + close(master); + return -1; + } + + if ((slave = open(ptr, O_RDWR)) < 0 || + ioctl(slave, I_PUSH, "ptem") < 0 || + ioctl(slave, I_PUSH, "ldterm") < 0 || + ioctl(slave, I_PUSH, "ttcompat") < 0) { + close(master); + close(slave); + return -1; + } + + *amaster = master; + *aslave = slave; + return 0; +#else + static char line[] = "/dev/ptyXX"; + register const char *cp1, *cp2; + + for (cp1 = "pqrsPQRS"; *cp1; cp1++) { + line[8] = *cp1; + for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) { + line[9] = *cp2; + if ((master = open(line, O_RDWR, 0)) == -1) { + if (errno == ENOENT) + return (-1); /* out of ptys */ + } else { + line[5] = 't'; + /* These will fail */ + (void) chown(line, getuid(), 0); + (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); +#ifdef HAVE_REVOKE + (void) revoke(line); +#endif + if ((slave = open(line, O_RDWR, 0)) != -1) { + *amaster = master; + *aslave = slave; + return 0; + } + (void) close(master); + line[5] = 'p'; + } + } + } + errno = ENOENT; /* out of ptys */ + return (-1); +#endif +} + + +/* + * XXX This is ugly + * We create and bind a socket, then fork off to another + * process, which connects to this socket, after which we + * exec the wanted program. If something (strange) happens, + * the accept() call could block us forever. + * + * do_pty = 0 Fork/exec inetd style + * do_pty = 1 Fork/exec using slirp.telnetd + * do_ptr = 2 Fork/exec using pty + */ +int +fork_exec(struct SLIRPsocket *so, char *ex, int do_pty) +{ + int s; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int opt; + int master; + char *argv[256]; +#if 0 + char buff[256]; +#endif + /* don't want to clobber the original */ + char *bptr; + char *curarg; + int c, i, ret; + + DEBUG_CALL("fork_exec"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("ex = %lx", (long)ex); + DEBUG_ARG("do_pty = %lx", (long)do_pty); + + if (do_pty == 2) { + if (slirp_openpty(&master, &s) == -1) { + lprint("Error: openpty failed: %s\n", strerror(errno)); + return 0; + } + } else { + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0 || + bind(s, (struct sockaddr *)&addr, addrlen) < 0 || + listen(s, 1) < 0) { + lprint("Error: inet socket: %s\n", strerror(errno)); + closesocket(s); + + return 0; + } + } + + switch(fork()) { + case -1: + lprint("Error: fork failed: %s\n", strerror(errno)); + close(s); + if (do_pty == 2) + close(master); + return 0; + + case 0: + /* Set the DISPLAY */ + if (do_pty == 2) { + (void) close(master); +#ifdef TIOCSCTTY /* XXXXX */ + (void) setsid(); + ioctl(s, TIOCSCTTY, (char *)NULL); +#endif + } else { + getsockname(s, (struct sockaddr *)&addr, &addrlen); + close(s); + /* + * Connect to the socket + * XXX If any of these fail, we're in trouble! + */ + s = socket(AF_INET, SOCK_STREAM, 0); + addr.sin_addr = loopback_addr; + do { + ret = connect(s, (struct sockaddr *)&addr, addrlen); + } while (ret < 0 && errno == EINTR); + } + +#if 0 + if (x_port >= 0) { +#ifdef HAVE_SETENV + sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + setenv("DISPLAY", buff, 1); +#else + sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + putenv(buff); +#endif + } +#endif + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + for (s = 3; s <= 255; s++) + close(s); + + i = 0; + bptr = strdup(ex); /* No need to free() this */ + if (do_pty == 1) { + /* Setup "slirp.telnetd -x" */ + argv[i++] = "slirp.telnetd"; + argv[i++] = "-x"; + argv[i++] = bptr; + } else + do { + /* Change the string into argv[] */ + curarg = bptr; + while (*bptr != ' ' && *bptr != (char)0) + bptr++; + c = *bptr; + *bptr++ = (char)0; + argv[i++] = strdup(curarg); + } while (c); + + argv[i] = 0; + execvp(argv[0], argv); + + /* Ooops, failed, let's tell the user why */ + { + char buff[256]; + + sprintf(buff, "Error: execvp of %s failed: %s\n", + argv[0], strerror(errno)); + write(2, buff, strlen(buff)+1); + } + close(0); close(1); close(2); /* XXX */ + exit(1); + + default: + if (do_pty == 2) { + close(s); + so->s = master; + } else { + /* + * XXX this could block us... + * XXX Should set a timer here, and if accept() doesn't + * return after X seconds, declare it a failure + * The only reason this will block forever is if socket() + * of connect() fail in the child process + */ + do { + so->s = accept(s, (struct sockaddr *)&addr, &addrlen); + } while (so->s < 0 && errno == EINTR); + closesocket(s); + opt = 1; + setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); + opt = 1; + setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); + } + fd_nonblock(so->s); + + /* Append the telnet options now */ + if (so->so_m != 0 && do_pty == 1) { + sbappend(so, so->so_m); + so->so_m = 0; + } + + return 1; + } +} + + +#if 0 +void +snooze_hup(int num) +{ + int s, ret; +#ifndef NO_UNIX_SOCKETS + struct sockaddr_un sock_un; +#endif + struct sockaddr_in sock_in; + char buff[256]; + + ret = -1; + if (slirp_socket_passwd) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + slirp_exit(1); + sock_in.sin_family = AF_INET; + sock_in.sin_addr.s_addr = slirp_socket_addr; + sock_in.sin_port = htons(slirp_socket_port); + if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) != 0) + slirp_exit(1); /* just exit...*/ + sprintf(buff, "kill %s:%d", slirp_socket_passwd, slirp_socket_unit); + write(s, buff, strlen(buff)+1); + } +#ifndef NO_UNIX_SOCKETS + else { + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + slirp_exit(1); + sock_un.sun_family = AF_UNIX; + strcpy(sock_un.sun_path, socket_path); + if (connect(s, (struct sockaddr *)&sock_un, + sizeof(sock_un.sun_family) + sizeof(sock_un.sun_path)) != 0) + slirp_exit(1); + sprintf(buff, "kill none:%d", slirp_socket_unit); + write(s, buff, strlen(buff)+1); + } +#endif + slirp_exit(0); +} + + +void +snooze(void) +{ + sigset_t s; + int i; + + /* Don't need our data anymore */ + /* XXX This makes SunOS barf */ +/* brk(0); */ + + /* Close all fd's */ + for (i = 255; i >= 0; i--) + close(i); + + signal(SIGQUIT, slirp_exit); + signal(SIGHUP, snooze_hup); + sigemptyset(&s); + + /* Wait for any signal */ + sigsuspend(&s); + + /* Just in case ... */ + exit(255); +} + +void +relay(int s) +{ + char buf[8192]; + int n; + fd_set readfds; + struct ttys *ttyp; + + /* Don't need our data anymore */ + /* XXX This makes SunOS barf */ +/* brk(0); */ + + signal(SIGQUIT, slirp_exit); + signal(SIGHUP, slirp_exit); + signal(SIGINT, slirp_exit); + signal(SIGTERM, slirp_exit); + + /* Fudge to get term_raw and term_restore to work */ + if (NULL == (ttyp = tty_attach (0, slirp_tty))) { + lprint ("Error: tty_attach failed in misc.c:relay()\r\n"); + slirp_exit (1); + } + ttyp->fd = 0; + ttyp->flags |= TTY_CTTY; + term_raw(ttyp); + + while (1) { + FD_ZERO(&readfds); + + FD_SET(0, &readfds); + FD_SET(s, &readfds); + + n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0); + + if (n <= 0) + slirp_exit(0); + + if (FD_ISSET(0, &readfds)) { + n = read(0, buf, 8192); + if (n <= 0) + slirp_exit(0); + n = writen(s, buf, n); + if (n <= 0) + slirp_exit(0); + } + + if (FD_ISSET(s, &readfds)) { + n = read(s, buf, 8192); + if (n <= 0) + slirp_exit(0); + n = writen(0, buf, n); + if (n <= 0) + slirp_exit(0); + } + } + + /* Just in case.... */ + exit(1); +} +#endif + + +void +add_emu(char *buff) +{ + u_int lport, fport; + u_int8_t tos = 0, emu = 0; + char buff1[256], buff2[256], buff4[128]; + char *buff3 = buff4; + struct emu_t *emup; + struct SLIRPsocket *so; + + if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) { + lprint("Error: Bad arguments\r\n"); + return; + } + + if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) { + lport = 0; + if (sscanf(buff1, "%d", &fport) != 1) { + lprint("Error: Bad first argument\r\n"); + return; + } + } + + if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) { + buff3 = 0; + if (sscanf(buff2, "%256s", buff1) != 1) { + lprint("Error: Bad second argument\r\n"); + return; + } + } + + if (buff3) { + if (strcmp(buff3, "lowdelay") == 0) + tos = IPTOS_LOWDELAY; + else if (strcmp(buff3, "throughput") == 0) + tos = IPTOS_THROUGHPUT; + else { + lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n"); + return; + } + } + + if (strcmp(buff1, "ftp") == 0) + emu = EMU_FTP; + else if (strcmp(buff1, "irc") == 0) + emu = EMU_IRC; + else if (strcmp(buff1, "none") == 0) + emu = EMU_NONE; /* ie: no emulation */ + else { + lprint("Error: Unknown service\r\n"); + return; + } + + /* First, check that it isn't already emulated */ + for (emup = tcpemu; emup; emup = emup->next) { + if (emup->lport == lport && emup->fport == fport) { + lprint("Error: port already emulated\r\n"); + return; + } + } + + /* link it */ + emup = (struct emu_t *)malloc(sizeof (struct emu_t)); + emup->lport = (u_int16_t)lport; + emup->fport = (u_int16_t)fport; + emup->tos = tos; + emup->emu = emu; + emup->next = tcpemu; + tcpemu = emup; + + /* And finally, mark all current sessions, if any, as being emulated */ + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + if ((lport && lport == ntohs(so->so_lport)) || + (fport && fport == ntohs(so->so_fport))) { + if (emu) + so->so_emu = emu; + if (tos) + so->so_iptos = tos; + } + } + + lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport); +} + +#ifdef BAD_SPRINTF +#undef vsprintf +#undef sprintf + +/* + * Some BSD-derived systems have a sprintf which returns char * + */ + +int +vsprintf_len(char *string, const char *format, va_list args) +{ + vsprintf(string, format, args); + return strlen(string); +} + +int +#ifdef __STDC__ +sprintf_len(char *string, const char *format, ...) +#else +sprintf_len(va_alist) va_dcl +#endif +{ + va_list args; +#ifdef __STDC__ + va_start(args, format); +#else + char *string; + char *format; + va_start(args); + string = va_arg(args, char *); + format = va_arg(args, char *); +#endif + vsprintf(string, format, args); + return strlen(string); +} + +#endif + +void +u_sleep(int usec) +{ + struct timeval t; + fd_set fdset; + + FD_ZERO(&fdset); + + t.tv_sec = 0; + t.tv_usec = usec * 1000; + + select(0, &fdset, &fdset, &fdset, &t); +} + +/* + * Set fd blocking and non-blocking + */ + +void +fd_nonblock(int fd) +{ +#if defined USE_FIONBIO && defined FIONBIO + ioctlsockopt_t opt = 1; + + ioctlsocket(fd, FIONBIO, &opt); +#else + int opt; + + opt = fcntl(fd, F_GETFL, 0); + opt |= O_NONBLOCK; + fcntl(fd, F_SETFL, opt); +#endif +} + +void +fd_block(int fd) +{ +#if defined USE_FIONBIO && defined FIONBIO + ioctlsockopt_t opt = 0; + + ioctlsocket(fd, FIONBIO, &opt); +#else + int opt; + + opt = fcntl(fd, F_GETFL, 0); + opt &= ~O_NONBLOCK; + fcntl(fd, F_SETFL, opt); +#endif +} + + +#if 0 +/* + * invoke RSH + */ +int +rsh_exec(so,ns, user, host, args) + struct SLIRPsocket *so; + struct SLIRPsocket *ns; + char *user; + char *host; + char *args; +{ + int fd[2]; + int fd0[2]; + int s; + char buff[256]; + + DEBUG_CALL("rsh_exec"); + DEBUG_ARG("so = %lx", (long)so); + + if (pipe(fd)<0) { + lprint("Error: pipe failed: %s\n", strerror(errno)); + return 0; + } +/* #ifdef HAVE_SOCKETPAIR */ +#if 1 + if (socketpair(PF_UNIX,SOCK_STREAM,0, fd0) == -1) { + close(fd[0]); + close(fd[1]); + lprint("Error: openpty failed: %s\n", strerror(errno)); + return 0; + } +#else + if (slirp_openpty(&fd0[0], &fd0[1]) == -1) { + close(fd[0]); + close(fd[1]); + lprint("Error: openpty failed: %s\n", strerror(errno)); + return 0; + } +#endif + + switch(fork()) { + case -1: + lprint("Error: fork failed: %s\n", strerror(errno)); + close(fd[0]); + close(fd[1]); + close(fd0[0]); + close(fd0[1]); + return 0; + + case 0: + close(fd[0]); + close(fd0[0]); + + /* Set the DISPLAY */ + if (x_port >= 0) { +#ifdef HAVE_SETENV + sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + setenv("DISPLAY", buff, 1); +#else + sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + putenv(buff); +#endif + } + + dup2(fd0[1], 0); + dup2(fd0[1], 1); + dup2(fd[1], 2); + for (s = 3; s <= 255; s++) + close(s); + + execlp("rsh","rsh","-l", user, host, args, NULL); + + /* Ooops, failed, let's tell the user why */ + + sprintf(buff, "Error: execlp of %s failed: %s\n", + "rsh", strerror(errno)); + write(2, buff, strlen(buff)+1); + close(0); close(1); close(2); /* XXX */ + exit(1); + + default: + close(fd[1]); + close(fd0[1]); + ns->s=fd[0]; + so->s=fd0[0]; + + return 1; + } +} +#endif diff --git a/src/win/host.c b/src/win/host.c new file mode 100644 index 0000000..6038dd7 --- /dev/null +++ b/src/win/host.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../host.h" +#include "../debug.h" + + +#ifndef AI_FQDN +# define AI_FQDN 0x00020000 +#endif + + +extern struct in_addr our_addr; /* host IP address */ +extern struct in_addr dns_addr; /* host DNS server */ +extern struct in_addr loopback_addr; /* host loopback address */ + + +int +inet_aton(const char *cp, struct in_addr *ia) +{ + uint32_t addr = inet_addr(cp); + + if (addr == 0xffffffff) + return 0; + ia->s_addr = addr; + + return 1; +} + + +int +host_get_info(struct in_addr *dns, char *hostname, char *domainname) +{ + struct addrinfo hints, *ai; + IP_ADDR_STRING *pIPAddr; + FIXED_INFO *FixedInfo; + struct in_addr addr; + ULONG BufLen; + DWORD ret; + + FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO)); + BufLen = sizeof(FIXED_INFO); + + if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) { + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, BufLen); + } + + if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) { + lprint("GetNetworkParams failed. ret = %08x\n", (u_int)ret); + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return -1; + } + + /* Set up hostname, domainname and IP address. */ + strcpy(hostname, FixedInfo->HostName); + strcpy(domainname, FixedInfo->DomainName); +strcpy(domainname, "homenet.lan"); + memset(&hints, 0x00, sizeof(hints)); + ai = NULL; +#ifdef AI_FQDN + hints.ai_flags = AI_FQDN; +#else + hints.ai_flags = AI_CANONNAME; +#endif + hints.ai_family = AF_INET; + if (! getaddrinfo(hostname, NULL, &hints, &ai)) { + our_addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr; + strcpy(hostname, ai->ai_canonname); + freeaddrinfo(ai); + } + if (our_addr.s_addr == 0) + our_addr.s_addr = loopback_addr.s_addr; + + lprint(" Hostname: %s.%s\n", hostname, domainname); + lprint(" IP address: %s\n", inet_ntoa(our_addr)); + + memset(&addr, 0x00, sizeof(addr)); + pIPAddr = &FixedInfo->DnsServerList; + inet_aton(pIPAddr->IpAddress.String, &addr); + *dns = addr; + + lprint(" DNS Servers:\n" ); + while (pIPAddr) { + lprint(" Address: %s\n", pIPAddr->IpAddress.String); + pIPAddr = pIPAddr->Next; + } + + if (FixedInfo) + GlobalFree(FixedInfo); + + return 0; +} + + +int +host_init(void) +{ + WSADATA Data; + + WSAStartup(MAKEWORD(2,0), &Data); + + return 0; +} + + +void +host_close(void) +{ + WSACleanup(); +} diff --git a/src/win/host.d b/src/win/host.d new file mode 100644 index 0000000..443c980 --- /dev/null +++ b/src/win/host.d @@ -0,0 +1 @@ +host.o: win/host.c win/../host.h win/../debug.h diff --git a/src/win/icons/varcem.ico b/src/win/icons/varcem.ico new file mode 100644 index 0000000..97f7f17 Binary files /dev/null and b/src/win/icons/varcem.ico differ diff --git a/src/win/libslirp.rc b/src/win/libslirp.rc new file mode 100644 index 0000000..8ae82de --- /dev/null +++ b/src/win/libslirp.rc @@ -0,0 +1,84 @@ +/* + * VARCem Virtual ARchaeological Computer EMulator. + * An emulator of (mostly) x86-based PC systems and devices, + * using the ISA,EISA,VLB,MCA and PCI system buses, roughly + * spanning the era between 1981 and 1995. + * + * This file is part of the VARCem Project. + * + * Resource info for the SLiRP library. + * + * Version: @(#)libslirp.rc 1.0.1 2018/11/02 + * + * Author: Fred N. van Kempen, + * + * Copyright 2018 Fred N. van Kempen. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the + * following conditions are met: + * + * 1. Redistributions of source code must retain the entire + * above notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names + * of its contributors may be used to endorse or promote + * products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + + +100 ICON DISCARDABLE "win/icons/varcem.ico" + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,1,0 + PRODUCTVERSION 1,0,1,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0409fde9" + BEGIN + VALUE "CompanyName", "IRC #VARCem on FreeNode" + VALUE "FileDescription", "SLiRP - Host Network Interface library." + VALUE "FileVersion", "1.0.1" + VALUE "InternalName", "libslirp" + VALUE "LegalCopyright", "Copyright 2017,2018 Fred N. van Kempen" + VALUE "OriginalFilename", "libslirp.dll" + VALUE "ProductName", "SLiRP Interface Library" + VALUE "ProductVersion", "1.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 65001 + END +END diff --git a/src/win/mingw/Makefile.MinGW b/src/win/mingw/Makefile.MinGW new file mode 100644 index 0000000..b25a8da --- /dev/null +++ b/src/win/mingw/Makefile.MinGW @@ -0,0 +1,312 @@ +# +# VARCem Virtual ARchaeological Computer EMulator. +# An emulator of (mostly) x86-based PC systems and devices, +# using the ISA,EISA,VLB,MCA and PCI system buses, roughly +# spanning the era between 1981 and 1995. +# +# This file is part of the VARCem Project. +# +# Makefile for Windows systems using the MinGW32 environment. +# +# Version: @(#)Makefile.mingw 1.0.1 2018/11/02 +# +# Author: Fred N. van Kempen, +# +# Copyright 2017,2018 Fred N. van Kempen. +# +# Redistribution and use in source and binary forms, with +# or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the entire +# above notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names +# of its contributors may be used to endorse or promote +# products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Defaults for several build options (possibly defined in a chained file.) +ifndef AUTODEP + AUTODEP := n +endif +ifndef CROSS + CROSS := n +endif +ifndef DEBUG + DEBUG := n +endif +ifndef LOGGING + LOGGING := n +endif +ifndef OPTIM + OPTIM := n +endif +ifndef X64 + X64 := n +endif + + +# Name of the library. +ifndef PROG + PROG := libslirp +endif +ifeq ($(DEBUG), y) + PROG := $(PROG)-d +endif + + +######################################################################### +# Nothing should need changing from here on.. # +######################################################################### +VPATH := win + + +# +# Select the required build environment. We have, uhm, many.. +# +ifneq ($(CROSS), n) + # Cross-compiling (under Linux), select proper version. + ifeq ($(X64), y) + MINGW := x86_64-w64-mingw32 + else + MINGW := i686-w64-mingw32 + endif + + ifeq ($(X64), y) + CPP := /usr/bin/$(MINGW)-g++ -m64 + CC := /usr/bin/$(MINGW)-gcc -m64 + else + CPP := /usr/bin/$(MINGW)-g++ -m32 + CC := /usr/bin/$(MINGW)-gcc -m32 + endif + PREPROC := /usr/bin/$(MINGW)-cpp + WINDRES := /usr/bin/$(MINGW)-windres + + SYSINC := -I/usr/$(MINGW)/include -Iwin/mingw/include + SYSLIB := -L/usr/$(MINGW)/lib +else + # Native compilation using MinGW for Windows. + ifeq ($(X64), y) + CPP := g++ -m64 + CC := gcc -m64 + else + CPP := g++ -m32 + CC := gcc -m32 + endif + PREPROC := cpp + WINDRES := windres + ifndef CAT + CAT := cat + endif + + SYSINC := -Iwin/mingw/include + SYSLIB := +endif + +DEPS = -MMD -MF $*.d -c $< +DEPFILE := win/mingw/.depends + +# Set up the correct toolchain flags. +OPTS := -D_CRT_NON_CONFORMING_SWPRINTFS \ + -D__USE_MINGW_ANSI_STDIO_X +AFLAGS := -msse2 -mfpmath=sse +RFLAGS := --input-format=rc -O coff +LDFLAGS := +ifdef BUILD + OPTS += -DBUILD=$(BUILD) +endif +ifdef COMMIT + OPTS += -DCOMMIT=0x$(COMMIT) +endif +ifdef UPSTREAM + OPTS += -DUPSTREAM=0x$(UPSTREAM) +endif +ifdef EXFLAGS + OPTS += $(EXFLAGS) +endif +ifdef EXINC + OPTS += -I$(EXINC) +endif + OPTS += $(SYSINC) +ifneq ($(CROSS), n) + OPTS += -DUSE_CROSS +endif +ifeq ($(X64), y) + ifeq ($(OPTIM), y) + DFLAGS := -march=native + else + DFLAGS := + endif +else + ifeq ($(OPTIM), y) + DFLAGS := -march=native + else + DFLAGS := -march=i686 + endif +endif +ifeq ($(DEBUG), y) + DFLAGS += -ggdb -D_DEBUG + RFLAGS += -D_DEBUG + AOPTIM := + ifndef COPTIM + COPTIM := -Og + endif +else + ifeq ($(OPTIM), y) + AOPTIM := -mtune=native + ifndef COPTIM + COPTIM := -O3 + endif + else + ifndef COPTIM + COPTIM := -O3 + endif + endif +endif +ifeq ($(RELEASE), y) + OPTS += -DRELEASE_BUILD + RFLAGS += -DRELEASE_BUILD +endif + +LIBS := -mwindows \ + -lversion -lcomctl32 -lwinmm +LIBS += -lws2_32 -lwsock32 -liphlpapi -lpsapi +LIBS += -static -lgcc +#ifneq ($(X64), y) +# LIBS += -Wl,--large-address-aware +#endif + + +# Final versions of the toolchain flags. +# FIXME: add the -Wsign-compare option soon! +CFLAGS := $(WX_FLAGS) $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \ + $(AFLAGS) -fomit-frame-pointer -mstackrealign \ + -Wall -Wundef #-Wshadow #-Wunused-parameter -Wmissing-declarations + +CXXFLAGS := $(WX_FLAGS) $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \ + $(AFLAGS) -fno-strict-aliasing -fvisibility=hidden \ + -fvisibility-inlines-hidden \ + -Wall -Wundef -Wunused-parameter -Wmissing-declarations \ + -Wno-ctor-dtor-privacy -Woverloaded-virtual + + +######################################################################### +# Create the (final) list of objects to build. # +######################################################################### + +OBJ := slirp.o host.o misc.o poll.o \ + cksum.o if.o mbuf.o queue.o sbuf.o socket.o \ + debug.o stats.o \ + arp.o bootp.o \ + ip_icmp.o ip_input.o ip_output.o \ + tcp_input.o tcp_output.o tcp_subr.o tcp_timer.o \ + udp.o +ifdef EXOBJ +OBJ += $(EXOBJ) +endif + + +# Build module rules. +ifeq ($(AUTODEP), y) +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $(DEPS) -c $< + +%.o: %.cpp + @echo $< + @$(CPP) $(CXXFLAGS) $(DEPS) -c $< +else +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) -c $< + +%.o: %.cpp + @echo $< + @$(CPP) $(CXXFLAGS) -c $< + +%.d: %.c $(wildcard $*.d) + @echo $< + @$(CC) $(CFLAGS) $(DEPS) -E $< >NUL + +%.d: %.cpp $(wildcard $*.d) + @echo $< + @$(CPP) $(CXXFLAGS) $(DEPS) -E $< >NUL +endif + + +all: $(PREBUILD) $(PROG).dll $(POSTBUILD) + + +$(PROG).res: win/libslirp.rc + @echo Processing $< + @$(WINDRES) $(RFLAGS) -i $< -o $@ + +$(PROG).dll: $(OBJ) $(PROG).res + @echo Linking $(PROG).dll .. + @$(CC) $(LDFLAGS) -shared -o $@ \ + $(OBJ) $(PROG).res $(LIBS) +#ifneq ($(DEBUG), y) +# @strip $(PROG).dll +#endif + + +clean: + @echo Cleaning objects.. + @-rm -f *.o + @-rm -f *.res + +clobber: clean + @echo Cleaning executables.. + @-rm -f *.d + @-rm -f *.dll + @-rm -f *.exe +ifeq ($(PROFILER), y) + @-rm -f *.map +endif +# @-rm -f $(DEPFILE) + +ifneq ($(AUTODEP), y) +depclean: + @-rm -f $(DEPFILE) + @echo Creating dependencies.. + @echo # Run "make depends" to re-create this file. >$(DEPFILE) + +depends: DEPOBJ=$(OBJ:%.o=%.d) +depends: depclean $(OBJ:%.o=%.d) + @$(CAT) $(DEPOBJ) >>$(DEPFILE) +# @-rm -f $(DEPOBJ) + +$(DEPFILE): +endif + + +# Module dependencies. +ifeq ($(AUTODEP), y) +#-include $(OBJ:%.o=%.d) (better, but sloooowwwww) +-include *.d +else +include $(wildcard $(DEPFILE)) +endif + + +# End of Makefile.mingw. diff --git a/src/win/msvc/Include/d3dx9tex.h b/src/win/msvc/Include/d3dx9tex.h new file mode 100644 index 0000000..60d8fc2 --- /dev/null +++ b/src/win/msvc/Include/d3dx9tex.h @@ -0,0 +1,300 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +// File: d3dx9tex.h +// Content: D3DX texturing APIs +// +////////////////////////////////////////////////////////////////////////////// + +//#include "d3dx9.h" + +#ifndef __D3DX9TEX_H__ +#define __D3DX9TEX_H__ + + +//---------------------------------------------------------------------------- +// D3DX_FILTER flags: +// ------------------ +// +// A valid filter must contain one of these values: +// +// D3DX_FILTER_NONE +// No scaling or filtering will take place. Pixels outside the bounds +// of the source image are assumed to be transparent black. +// D3DX_FILTER_POINT +// Each destination pixel is computed by sampling the nearest pixel +// from the source image. +// D3DX_FILTER_LINEAR +// Each destination pixel is computed by linearly interpolating between +// the nearest pixels in the source image. This filter works best +// when the scale on each axis is less than 2. +// D3DX_FILTER_TRIANGLE +// Every pixel in the source image contributes equally to the +// destination image. This is the slowest of all the filters. +// D3DX_FILTER_BOX +// Each pixel is computed by averaging a 2x2(x2) box pixels from +// the source image. Only works when the dimensions of the +// destination are half those of the source. (as with mip maps) +// +// And can be OR'd with any of these optional flags: +// +// D3DX_FILTER_MIRROR_U +// Indicates that pixels off the edge of the texture on the U-axis +// should be mirrored, not wraped. +// D3DX_FILTER_MIRROR_V +// Indicates that pixels off the edge of the texture on the V-axis +// should be mirrored, not wraped. +// D3DX_FILTER_MIRROR_W +// Indicates that pixels off the edge of the texture on the W-axis +// should be mirrored, not wraped. +// D3DX_FILTER_MIRROR +// Same as specifying D3DX_FILTER_MIRROR_U | D3DX_FILTER_MIRROR_V | +// D3DX_FILTER_MIRROR_V +// D3DX_FILTER_DITHER +// Dithers the resulting image using a 4x4 order dither pattern. +// D3DX_FILTER_SRGB_IN +// Denotes that the input data is in sRGB (gamma 2.2) colorspace. +// D3DX_FILTER_SRGB_OUT +// Denotes that the output data is in sRGB (gamma 2.2) colorspace. +// D3DX_FILTER_SRGB +// Same as specifying D3DX_FILTER_SRGB_IN | D3DX_FILTER_SRGB_OUT +// +//---------------------------------------------------------------------------- + +#define D3DX_FILTER_NONE (1 << 0) +#define D3DX_FILTER_POINT (2 << 0) +#define D3DX_FILTER_LINEAR (3 << 0) +#define D3DX_FILTER_TRIANGLE (4 << 0) +#define D3DX_FILTER_BOX (5 << 0) + +#define D3DX_FILTER_MIRROR_U (1 << 16) +#define D3DX_FILTER_MIRROR_V (2 << 16) +#define D3DX_FILTER_MIRROR_W (4 << 16) +#define D3DX_FILTER_MIRROR (7 << 16) + +#define D3DX_FILTER_DITHER (1 << 19) +#define D3DX_FILTER_DITHER_DIFFUSION (2 << 19) + +#define D3DX_FILTER_SRGB_IN (1 << 21) +#define D3DX_FILTER_SRGB_OUT (2 << 21) +#define D3DX_FILTER_SRGB (3 << 21) + + +//----------------------------------------------------------------------------- +// D3DX_SKIP_DDS_MIP_LEVELS is used to skip mip levels when loading a DDS file: +//----------------------------------------------------------------------------- + +#define D3DX_SKIP_DDS_MIP_LEVELS_MASK 0x1F +#define D3DX_SKIP_DDS_MIP_LEVELS_SHIFT 26 +#define D3DX_SKIP_DDS_MIP_LEVELS(levels, filter) ((((levels) & D3DX_SKIP_DDS_MIP_LEVELS_MASK) << D3DX_SKIP_DDS_MIP_LEVELS_SHIFT) | ((filter) == D3DX_DEFAULT ? D3DX_FILTER_BOX : (filter))) + + + + +//---------------------------------------------------------------------------- +// D3DX_NORMALMAP flags: +// --------------------- +// These flags are used to control how D3DXComputeNormalMap generates normal +// maps. Any number of these flags may be OR'd together in any combination. +// +// D3DX_NORMALMAP_MIRROR_U +// Indicates that pixels off the edge of the texture on the U-axis +// should be mirrored, not wraped. +// D3DX_NORMALMAP_MIRROR_V +// Indicates that pixels off the edge of the texture on the V-axis +// should be mirrored, not wraped. +// D3DX_NORMALMAP_MIRROR +// Same as specifying D3DX_NORMALMAP_MIRROR_U | D3DX_NORMALMAP_MIRROR_V +// D3DX_NORMALMAP_INVERTSIGN +// Inverts the direction of each normal +// D3DX_NORMALMAP_COMPUTE_OCCLUSION +// Compute the per pixel Occlusion term and encodes it into the alpha. +// An Alpha of 1 means that the pixel is not obscured in anyway, and +// an alpha of 0 would mean that the pixel is completly obscured. +// +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +#define D3DX_NORMALMAP_MIRROR_U (1 << 16) +#define D3DX_NORMALMAP_MIRROR_V (2 << 16) +#define D3DX_NORMALMAP_MIRROR (3 << 16) +#define D3DX_NORMALMAP_INVERTSIGN (8 << 16) +#define D3DX_NORMALMAP_COMPUTE_OCCLUSION (16 << 16) + + + + +//---------------------------------------------------------------------------- +// D3DX_CHANNEL flags: +// ------------------- +// These flags are used by functions which operate on or more channels +// in a texture. +// +// D3DX_CHANNEL_RED +// Indicates the red channel should be used +// D3DX_CHANNEL_BLUE +// Indicates the blue channel should be used +// D3DX_CHANNEL_GREEN +// Indicates the green channel should be used +// D3DX_CHANNEL_ALPHA +// Indicates the alpha channel should be used +// D3DX_CHANNEL_LUMINANCE +// Indicates the luminaces of the red green and blue channels should be +// used. +// +//---------------------------------------------------------------------------- + +#define D3DX_CHANNEL_RED (1 << 0) +#define D3DX_CHANNEL_BLUE (1 << 1) +#define D3DX_CHANNEL_GREEN (1 << 2) +#define D3DX_CHANNEL_ALPHA (1 << 3) +#define D3DX_CHANNEL_LUMINANCE (1 << 4) + + + + +//---------------------------------------------------------------------------- +// D3DXIMAGE_FILEFORMAT: +// --------------------- +// This enum is used to describe supported image file formats. +// +//---------------------------------------------------------------------------- + +typedef enum _D3DXIMAGE_FILEFORMAT +{ + D3DXIFF_BMP = 0, + D3DXIFF_JPG = 1, + D3DXIFF_TGA = 2, + D3DXIFF_PNG = 3, + D3DXIFF_DDS = 4, + D3DXIFF_PPM = 5, + D3DXIFF_DIB = 6, + D3DXIFF_HDR = 7, //high dynamic range formats + D3DXIFF_PFM = 8, // + D3DXIFF_FORCE_DWORD = 0x7fffffff + +} D3DXIMAGE_FILEFORMAT; + + +#if 0 +//---------------------------------------------------------------------------- +// LPD3DXFILL2D and LPD3DXFILL3D: +// ------------------------------ +// Function types used by the texture fill functions. +// +// Parameters: +// pOut +// Pointer to a vector which the function uses to return its result. +// X,Y,Z,W will be mapped to R,G,B,A respectivly. +// pTexCoord +// Pointer to a vector containing the coordinates of the texel currently +// being evaluated. Textures and VolumeTexture texcoord components +// range from 0 to 1. CubeTexture texcoord component range from -1 to 1. +// pTexelSize +// Pointer to a vector containing the dimensions of the current texel. +// pData +// Pointer to user data. +// +//---------------------------------------------------------------------------- + +typedef VOID (WINAPI *LPD3DXFILL2D)(D3DXVECTOR4 *pOut, + CONST D3DXVECTOR2 *pTexCoord, CONST D3DXVECTOR2 *pTexelSize, LPVOID pData); + +typedef VOID (WINAPI *LPD3DXFILL3D)(D3DXVECTOR4 *pOut, + CONST D3DXVECTOR3 *pTexCoord, CONST D3DXVECTOR3 *pTexelSize, LPVOID pData); +#endif + + + +//---------------------------------------------------------------------------- +// D3DXIMAGE_INFO: +// --------------- +// This structure is used to return a rough description of what the +// the original contents of an image file looked like. +// +// Width +// Width of original image in pixels +// Height +// Height of original image in pixels +// Depth +// Depth of original image in pixels +// MipLevels +// Number of mip levels in original image +// Format +// D3D format which most closely describes the data in original image +// ResourceType +// D3DRESOURCETYPE representing the type of texture stored in the file. +// D3DRTYPE_TEXTURE, D3DRTYPE_VOLUMETEXTURE, or D3DRTYPE_CUBETEXTURE. +// ImageFileFormat +// D3DXIMAGE_FILEFORMAT representing the format of the image file. +// +//---------------------------------------------------------------------------- + +typedef struct _D3DXIMAGE_INFO +{ + UINT Width; + UINT Height; + UINT Depth; + UINT MipLevels; + D3DFORMAT Format; + D3DRESOURCETYPE ResourceType; + D3DXIMAGE_FILEFORMAT ImageFileFormat; + +} D3DXIMAGE_INFO; + + + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + + +//---------------------------------------------------------------------------- +// D3DXSaveSurfaceToFile: +// ---------------------- +// Save a surface to a image file. +// +// Parameters: +// pDestFile +// File name of the destination file +// DestFormat +// D3DXIMAGE_FILEFORMAT specifying file format to use when saving. +// pSrcSurface +// Source surface, containing the image to be saved +// pSrcPalette +// Source palette of 256 colors, or NULL +// pSrcRect +// Source rectangle, or NULL for the entire image +// +//---------------------------------------------------------------------------- + +HRESULT WINAPI + D3DXSaveSurfaceToFileA( + LPCSTR pDestFile, + D3DXIMAGE_FILEFORMAT DestFormat, + LPDIRECT3DSURFACE9 pSrcSurface, + CONST PALETTEENTRY* pSrcPalette, + CONST RECT* pSrcRect); + +HRESULT WINAPI + D3DXSaveSurfaceToFileW( + LPCWSTR pDestFile, + D3DXIMAGE_FILEFORMAT DestFormat, + LPDIRECT3DSURFACE9 pSrcSurface, + CONST PALETTEENTRY* pSrcPalette, + CONST RECT* pSrcRect); + +#ifdef UNICODE +#define D3DXSaveSurfaceToFile D3DXSaveSurfaceToFileW +#else +#define D3DXSaveSurfaceToFile D3DXSaveSurfaceToFileA +#endif + +#ifdef __cplusplus +} +#endif //__cplusplus + + +#endif //__D3DX9TEX_H__ diff --git a/src/win/msvc/Lib/x64/d3dx9.lib b/src/win/msvc/Lib/x64/d3dx9.lib new file mode 100644 index 0000000..8587d5b Binary files /dev/null and b/src/win/msvc/Lib/x64/d3dx9.lib differ diff --git a/src/win/msvc/Lib/x86/d3dx9.lib b/src/win/msvc/Lib/x86/d3dx9.lib new file mode 100644 index 0000000..11853bf Binary files /dev/null and b/src/win/msvc/Lib/x86/d3dx9.lib differ diff --git a/src/win/msvc/Makefile.VC b/src/win/msvc/Makefile.VC new file mode 100644 index 0000000..b4829a0 --- /dev/null +++ b/src/win/msvc/Makefile.VC @@ -0,0 +1,854 @@ +# +# VARCem Virtual ARchaeological Computer EMulator. +# An emulator of (mostly) x86-based PC systems and devices, +# using the ISA,EISA,VLB,MCA and PCI system buses, roughly +# spanning the era between 1981 and 1995. +# +# This file is part of the VARCem Project. +# +# Makefile for Windows using Visual Studio 2015. +# +# Version: @(#)Makefile.VC 1.0.52 2018/10/30 +# +# Author: Fred N. van Kempen, +# +# Copyright 2017,2018 Fred N. van Kempen. +# +# Redistribution and use in source and binary forms, with +# or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the entire +# above notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names +# of its contributors may be used to endorse or promote +# products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Various compile-time options. +ifndef STUFF + STUFF := +endif + +# Add feature selections here. +ifndef EXTRAS + EXTRAS := +endif + + +# Defaults for several build options (possibly defined in a chained file.) +ifndef AUTODEP + AUTODEP := n +endif +ifndef CRASHDUMP + CRASHDUMP := n +endif +ifndef CROSS + CROSS := n +endif +ifndef DEBUG + DEBUG := n +endif +ifndef LOGGING + LOGGING := n +endif +ifndef PROFILER + PROFILER := n +endif +ifndef OPTIM + OPTIM := n +endif +ifndef RELEASE + RELEASE := n +endif +ifndef X64 + X64 := n +endif +ifndef DYNAREC + DYNAREC := y +endif +ifndef WX + WX := n +endif +ifndef USB + USB := n +endif +ifndef SDL + SDL := n +endif +ifndef D2D + D2D := n +endif +ifndef VNC + VNC := n +endif +ifndef RDP + RDP := n +endif +ifndef PNG + PNG := n +endif +ifndef DEV_BUILD + DEV_BUILD := n +endif +ifndef DEV_BRANCH + DEV_BRANCH := n +endif +ifndef AMD_K + AMD_K := n +endif +ifndef LASERXT + LASERXT := n +endif +ifndef PORTABLE3 + PORTABLE3 := n +endif +ifndef I686 + I686 := n +endif +ifndef OPENAL + OPENAL := y +endif +ifndef FLUIDSYNTH + FLUIDSYNTH := y +endif +ifndef MUNT + MUNT := y +endif +ifndef PAS16 + PAS16 := n +endif +ifndef XL24 + XL24 := n +endif +ifndef WONDER + WONDER := n +endif +ifndef HOSTCD + HOSTCD := n +endif +ifndef PRINTER + PRINTER := n +endif + + +# Name of the executable. +NETIF := pcap_if +ifndef PROG + ifneq ($(WX), n) + PROG := WxVARCem + else + PROG := VARCem + endif +endif +ifeq ($(DEBUG), y) + PROG := $(PROG)-d + NETIF := $(NETIF)-d + LOGGING := y +else + ifeq ($(LOGGING), y) + PROG := $(PROG)-l + endif +endif + + +# Which modules to include a development build. +ifeq ($(DEV_BUILD), y) + CRASHDUMP := y + DEV_BRANCH := y + AMD_K := y + LASERXT := y + PORTABLE3 := y + I686 := y + PAS16 := y + XL24 := y + WONDER := y + HOSTCD := y + PRINTER := y +endif + + +# WxWidgets basic info. Extract using the config program. +ifneq ($(WX), n) + EXPATH += wx + WX_CONFIG := wx-config.exe + ifeq ($(WX), y) + WX_PATH := C:/MinGW32/WxWidgets + WX_FLAGS := -I$(WX_PATH)/lib/wx/include/msw-unicode-3.0 \ + -I$(WX_PATH)/include/wx-3.0 \ + -D__WXMSW__ -DWX_PRECOMP -D_FILE_OFFSET_BITS=64 -pthread +# -lwx_mswu_gl-3.0 -lwxtiff-3.0 -llzma + WX_LIBS := -mthreads -L$(WX_PATH)/lib \ + -lwx_mswu-3.0.dll \ + -lrpcrt4 -loleaut32 -lole32 -luuid \ + -lwinspool -lwinmm -lshell32 -lcomctl32 \ + -lcomdlg32 -ladvapi32 -lwsock32 -lgdi32 + endif + ifeq ($(WX), static) + WX_PATH := C:/MinGW32/WxWidgets + WX_FLAGS := -I$(WX_PATH)/lib/wx/include/msw-unicode-3.0 \ + -I$(WX_PATH)/include/wx-3.0 \ + -D__WXMSW__ -DWX_PRECOMP -D_FILE_OFFSET_BITS=64 -pthread +# -lwx_mswu_gl-3.0 -lwxtiff-3.0 -llzma + WX_LIBS := -mthreads -L$(WX_PATH)/lib \ + -lwx_mswu-3.0 -lwxscintilla-3.0 \ + -lwxjpeg-3.0 -lwxpng-3.0 -lwxzlib-3.0 \ + -lwxregexu-3.0 -lwxexpat-3.0 \ + -lrpcrt4 -loleaut32 -lole32 -luuid \ + -lwinspool -lwinmm -lshell32 -lcomctl32 \ + -lcomdlg32 -ladvapi32 -lwsock32 -lgdi32 + endif +endif + + +######################################################################### +# Nothing should need changing from here on.. # +######################################################################### + +VPATH := $(EXPATH) . cpu \ + devices \ + devices/cdrom devices/disk devices/floppy \ + devices/floppy/lzf devices/input devices/input/game \ + devices/network devices/network/slirp devices/ports \ + devices/printer devices/sio devices/system devices/scsi \ + devices/misc \ + devices/sound \ + devices/sound/munt devices/sound/munt/c_interface \ + devices/sound/munt/sha1 devices/sound/munt/srchelper \ + devices/sound/resid-fp \ + devices/video \ + machines ui win + + +# +# Select the required build environment. +# +VCOPTS := -D_CRT_SECURE_NO_WARNINGS -D__MSC__ +ifeq ($(X64), y) + ARCH := x64 + CPP := cl -nologo + CC := cl -nologo +else + ARCH := x86 + CPP := cl -nologo + CC := cl -nologo #-TP +endif +PREPROC := cl -nologo -EP +MCPP := mcpp.exe +LINK := link -nologo +WINDRES := rc -nologo -r -d_MSC_VER + +SYSINC := -Iwin/msvc/Include -Iwin/mingw/include +SYSLIB := + +DEPS = -MMD -MF $*.d +DEPFILE := win\msvc\.depends + +# Set up the correct toolchain flags. +OPTS := $(EXTRAS) $(STUFF) $(VCOPTS) \ + -D_USE_MATH_DEFINES \ + -D_CRT_NONSTDC_NO_DEPRECATE \ + -D_WINSOCK_DEPRECATED_NO_WARNINGS \ + -D_CRT_SECURE_NO_WARNINGS \ + -D_CRT_STDIO_ISO_WIDE_SPECIFIERS \ + $(WPCAPINC) $(SYSINC) +AFLAGS := #/arch:SSE2 +RFLAGS := /n +COPTS := -W3 +CXXOPTS := -EHsc +DOPTS := +ifeq ($(X64), y) +LOPTS := -MACHINE:$(ARCH) -LIBPATH:win\msvc\Lib\$(ARCH) +LOPTS_C := -SUBSYSTEM:CONSOLE +LOPTS_W := -SUBSYSTEM:WINDOWS +else +LOPTS := -MACHINE:$(ARCH) -LIBPATH:win\msvc\Lib\$(ARCH) +LOPTS_C := -SUBSYSTEM:CONSOLE,5.01 +LOPTS_W := -SUBSYSTEM:WINDOWS,5.01 +endif +ifdef BUILD + OPTS += -DBUILD=$(BUILD) +endif +ifdef COMMIT + OPTS += -DCOMMIT=0x$(COMMIT) +endif +ifdef UPSTREAM + OPTS += -DUPSTREAM=0x$(UPSTREAM) +endif +ifdef EXFLAGS + OPTS += $(EXFLAGS) +endif +ifdef EXINC + OPTS += -I$(EXINC) +endif +OPTS += $(SYSINC) +ifeq ($(OPTIM), y) + AFLAGS := /arch:AVX2 +endif +ifeq ($(DEBUG), y) + OPTS += -D_DEBUG + DOPTS += -MTd -Gs -Zi + RFLAGS += -D_DEBUG + LOPTS += -debug + ifndef COPTIM + COPTIM := -Od + endif +else + LOPTS += -LTCG + DOPTS := -MT -GL + ifndef COPTIM + COPTIM := -O2 + endif +endif +ifeq ($(LOGGING), y) + OPTS += -D_LOGGING + RFLAGS += -D_LOGGING +endif +ifeq ($(CRASHDUMP), y) + OPTS += -DUSE_CRASHDUMP +endif +ifeq ($(RELEASE), y) + OPTS += -DRELEASE_BUILD + RFLAGS += -DRELEASE_BUILD +endif +ifeq ($(PROFILER), y) + LOPTS += /MAP +endif +ifeq ($(VRAMDUMP), y) + OPTS += -DENABLE_VRAM_DUMP + RFLAGS += -DENABLE_VRAM_DUMP +endif +ifeq ($(X64), y) + PLATCG := codegen_x86-64.obj + CGOPS := codegen_ops_x86-64.h + VCG := vid_voodoo_codegen_x86-64.h +else + PLATCG := codegen_x86.obj + CGOPS := codegen_ops_x86.h + VCG := vid_voodoo_codegen_x86.h +endif +LIBS := ddraw.lib dinput8.lib dxguid.lib d3d9.lib d3dx9.lib \ + version.lib comctl32.lib winmm.lib comdlg32.lib \ + advapi32.lib gdi32.lib shell32.lib user32.lib \ + ws2_32.lib wsock32.lib iphlpapi.lib psapi.lib +ifeq ($(DEBUG), y) +LIBS += libcmtd.lib libvcruntimed.lib libucrtd.lib +endif + + +# Optional modules. +MISCOBJ := +ifeq ($(DYNAREC), y) + OPTS += -DUSE_DYNAREC + RFLAGS += -DUSE_DYNAREC + DYNARECOBJ := 386_dynarec_ops.obj \ + codegen.obj \ + codegen_ops.obj \ + codegen_timing_common.obj codegen_timing_486.obj \ + codegen_timing_686.obj codegen_timing_pentium.obj \ + codegen_timing_winchip.obj $(PLATCG) +endif + +ifeq ($(OPENAL), y) + OPTS += -DUSE_OPENAL +endif + +ifeq ($(FLUIDSYNTH), y) + OPTS += -DUSE_FLUIDSYNTH + MISCOBJ += midi_fluidsynth.obj +endif + +ifeq ($(MUNT), y) + OPTS += -DUSE_MUNT + MISCOBJ += midi_mt32.obj \ + Analog.obj BReverbModel.obj File.obj FileStream.obj \ + LA32Ramp.obj LA32FloatWaveGenerator.obj \ + LA32WaveGenerator.obj MidiStreamParser.obj Part.obj \ + Partial.obj PartialManager.obj Poly.obj ROMInfo.obj \ + SampleRateConverter_dummy.obj Synth.obj Tables.obj \ + TVA.obj TVF.obj TVP.obj sha1.obj c_interface.obj +endif + +# N=no, Y=yes,linked, D=yes,dynamic, S=yes,static +ifneq ($(SDL), n) + OPTS += -DUSE_SDL + RFLAGS += -DUSE_SDL + ifneq ($(SDL_PATH), ) + OPTS += -I$(SDL_PATH)/include/msvc -I$(SDL_PATH)/include + ifeq ($(X64), y) + LOPTS += -LIBPATH:$(SDL_PATH)\lib\msvc\x64 + else + LOPTS += -LIBPATH:$(SDL_PATH)\lib\msvc\x86 + endif + endif + ifeq ($(SDL), y) + LIBS += sdl2.lib + endif + SDLOBJ := win_sdl.obj +endif + +# N=no, Y=yes,linked, D=yes,dynamic, S=yes,static +ifneq ($(D2D), n) + ifeq ($(D2D), d) + OPTS += -DUSE_D2D=2 + else + OPTS += -DUSE_D2D=1 + endif + RFLAGS += -DUSE_D2D + ifneq ($(D2D_PATH), ) + OPTS += -I$(D2D_PATH)/include/msvc -I$(D2D_PATH)/include + ifeq ($(X64), y) + LOPTS += -LIBPATH:$(D2D_PATH)\lib\msvc\x64 + else + LOPTS += -LIBPATH:$(D2D_PATH)\lib\msvc\x86 + endif + endif + ifeq ($(D2D), y) + LIBS += d2d1.lib + endif + D2DOBJ := win_d2d.obj +endif + +# N=no, Y=yes,linked, D=yes,dynamic, S=yes,static +ifneq ($(VNC), n) + OPTS += -DUSE_VNC + RFLAGS += -DUSE_VNC + ifneq ($(VNC_PATH), ) + OPTS += -I$(VNC_PATH)/include/msvc -I$(VNC_PATH)/include + ifeq ($(X64), y) + LOPTS += -LIBPATH:$(VNC_PATH)\lib\x64 + else + LOPTS += -LIBPATH:$(VNC_PATH)\lib\x86 + endif + endif + ifeq ($(VNC), y) + LIBS += libvncserver.lib + endif + MISCOBJ += vnc.obj vnc_keymap.obj +endif + +# N=no, Y=yes,linked, D=yes,dynamic, S=yes,static +ifneq ($(RDP), n) + OPTS += -DUSE_RDP + RFLAGS += -DUSE_RDP + ifneq ($(RDP_PATH), ) + OPTS += -I$(RDP_PATH)/include/msvc -I$(RDP_PATH)/include + ifeq ($(X64), y) + LOPTS += -LIBPATH:$(RDP_PATH)\lib\x64 + else + LOPTS += -LIBPATH:$(RDP_PATH)\lib\x86 + endif + endif + ifeq ($(RDP), y) + LIBS += rdpsrvr.lib + endif + MISCOBJ += rdp.obj +endif + +# N=no, Y=yes,linked, D=yes,dynamic, S=yes,static +ifneq ($(PNG), n) + ifeq ($(PNG), d) + OPTS += -DUSE_LIBPNG=2 + else + OPTS += -DUSE_LIBPNG=1 + endif + RFLAGS += -DUSE_LIBPNG + ifneq ($(PNG_PATH), ) + OPTS += -I$(PNG_PATH)/include/msvc -I$(PNG_PATH)/include + ifeq ($(X64), y) + LOPTS += -LIBPATH:$(PNG_PATH)\lib\x64 + else + LOPTS += -LIBPATH:$(PNG_PATH)\lib\x86 + endif + endif + ifeq ($(PNG), y) + LIBS += libpng16_dll.lib #zlib.lib + endif + MISCOBJ += png.obj +endif + +# N=no, Y=yes,linked, D=yes,dynamic, S=yes,static +ifneq ($(WX), n) + OPTS += -DUSE_WX $(WX_FLAGS) + LIBS += $(WX_LIBS) -lm + UIOBJ := wx_main.obj wx_ui.obj wx_stbar.obj wx_render.obj +else + UIOBJ := win_ui.obj \ + win_ddraw.obj win_d3d.obj $(SDLOBJ) $(D2DOBJ) \ + win_dialog.obj win_about.obj win_settings.obj \ + win_devconf.obj win_snd_gain.obj win_new_image.obj +endif + + +# Options for the DEV branch. +ifeq ($(DEV_BRANCH), y) + OPTS += -DDEV_BRANCH + DEVBROBJ := + + ifeq ($(AMD_K), y) + OPTS += -DUSE_AMD_K + endif + + ifeq ($(LASERXT), y) + OPTS += -DUSE_LASERXT + DEVBROBJ += m_xt_laserxt.obj + endif + + ifeq ($(PORTABLE3), y) + OPTS += -DUSE_PORTABLE3 + endif + + ifeq ($(I686), y) + OPTS += -DUSE_I686 + endif + + ifeq ($(PAS16), y) + OPTS += -DUSE_PAS16 + DEVBROBJ += snd_pas16.obj + endif + + ifeq ($(XL24), y) + OPTS += -DUSE_XL24 + endif + + ifeq ($(WONDER), y) + OPTS += -DUSE_WONDER + endif + + ifeq ($(HOSTCD), y) + OPTS += -DUSE_HOST_CDROM + endif + + ifeq ($(PRINTER), y) + OPTS += -DUSE_PRINTER + DEVBROBJ += prt_parallel.obj + endif +endif + + +# Final versions of the toolchain flags. +LDFLAGS := $(LOPTS) +CFLAGS := $(WX_FLAGS) $(OPTS) $(COPTS) $(COPTIM) $(DOPTS) $(AFLAGS) +CXXFLAGS := $(WX_FLAGS) $(OPTS) $(CXXOPTS) $(COPTS) $(COPTIM) $(DOPTS) $(AFLAGS) + + +######################################################################### +# Create the (final) list of objects to build. # +######################################################################### + +RESDLL := VARCem-$(LANG) + +MAINOBJ := pc.obj config.obj misc.obj random.obj timer.obj io.obj \ + mem.obj rom.obj rom_load.obj device.obj nvr.obj + +UIOBJ += ui_main.obj ui_lang.obj ui_stbar.obj ui_vidapi.obj \ + ui_cdrom.obj ui_new_image.obj ui_misc.obj + +SYSOBJ := dma.obj nmi.obj pic.obj pit.obj ppi.obj pci.obj mca.obj \ + mcr.obj memregs.obj nvr_at.obj nvr_ps2.obj + +CPUOBJ := cpu.obj cpu_table.obj \ + 808x.obj 386.obj x86seg.obj x87.obj \ + 386_dynarec.obj $(DYNARECOBJ) + +MCHOBJ := machine.obj machine_table.obj \ + m_xt.obj m_xt_compaq.obj \ + m_xt_t1000.obj m_xt_t1000_vid.obj \ + m_xt_xi8088.obj \ + m_pcjr.obj \ + m_amstrad.obj m_europc.obj \ + m_olivetti_m24.obj m_tandy.obj \ + m_at.obj \ + m_at_ali1429.obj m_at_commodore.obj \ + m_at_neat.obj m_at_headland.obj \ + m_at_t3100e.obj m_at_t3100e_vid.obj \ + m_ps1.obj m_ps1_hdc.obj \ + m_ps2_isa.obj m_ps2_mca.obj \ + m_at_opti495.obj m_at_scat.obj \ + m_at_compaq.obj m_at_wd76c10.obj \ + m_at_sis_85c471.obj m_at_sis_85c496.obj \ + m_at_4x0.obj + +INTELOBJ := intel.obj \ + intel_flash.obj \ + intel_sio.obj \ + intel_piix.obj + +DEVOBJ := bugger.obj \ + isamem.obj isartc.obj \ + game.obj game_dev.obj \ + parallel.obj parallel_dev.obj \ + prt_text.obj prt_cpmap.obj prt_escp.obj \ + serial.obj \ + sio_fdc37c66x.obj sio_fdc37c669.obj sio_fdc37c93x.obj \ + sio_pc87306.obj sio_w83877f.obj sio_um8669f.obj \ + keyboard.obj \ + keyboard_xt.obj keyboard_at.obj \ + mouse.obj \ + mouse_serial.obj mouse_ps2.obj mouse_bus.obj \ + joystick.obj \ + js_standard.obj js_ch_fs_pro.obj \ + js_sw_pad.obj js_tm_fcs.obj + +FDDOBJ := fdc.obj \ + fdd.obj \ + fdd_common.obj fdd_86f.obj \ + fdd_fdi.obj fdi2raw.obj lzf_c.obj lzf_d.obj \ + fdd_imd.obj fdd_img.obj fdd_json.obj fdd_td0.obj + +HDDOBJ := hdd.obj \ + hdd_image.obj hdd_table.obj \ + hdc.obj \ + hdc_st506_xt.obj hdc_st506_at.obj \ + hdc_esdi_at.obj hdc_esdi_mca.obj \ + hdc_ide_ata.obj hdc_ide_xta.obj hdc_xtide.obj + +CDROMOBJ := cdrom.obj \ + cdrom_speed.obj \ + cdrom_dosbox.obj cdrom_image.obj + +ZIPOBJ := zip.obj + +ifeq ($(USB), y) +USBOBJ := usb.obj +endif + +SCSIOBJ := scsi.obj \ + scsi_device.obj scsi_disk.obj scsi_cdrom.obj \ + scsi_x54x.obj scsi_aha154x.obj scsi_buslogic.obj \ + scsi_ncr5380.obj scsi_ncr53c810.obj + +NETOBJ := network.obj \ + net_pcap.obj \ + net_slirp.obj \ + slirp.obj slirp_misc.obj \ + cksum.obj debug.obj if.obj mbuf.obj queue.obj sbuf.obj \ + socket.obj \ + arp.obj bootp.obj \ + ip_icmp.obj ip_input.obj ip_output.obj \ + tcp_input.obj tcp_output.obj tcp_subr.obj tcp_timer.obj \ + udp.obj \ + net_dp8390.obj \ + net_ne2000.obj net_wd80x3.obj net_3c503.obj + +SNDOBJ := sound.obj \ + sound_dev.obj \ + openal.obj \ + snd_opl.obj snd_dbopl.obj \ + dbopl.obj nukedopl.obj \ + snd_resid.obj \ + convolve.obj convolve-sse.obj envelope.obj extfilt.obj \ + filter.obj pot.obj sid.obj voice.obj wave6581__ST.obj \ + wave6581_P_T.obj wave6581_PS_.obj wave6581_PST.obj \ + wave8580__ST.obj wave8580_P_T.obj wave8580_PS_.obj \ + wave8580_PST.obj wave.obj \ + midi.obj midi_system.obj \ + snd_speaker.obj \ + snd_lpt_dac.obj snd_lpt_dss.obj \ + snd_adlib.obj snd_adlibgold.obj snd_ad1848.obj snd_audiopci.obj \ + snd_cms.obj \ + snd_gus.obj \ + snd_sb.obj snd_sb_dsp.obj \ + snd_emu8k.obj snd_mpu401.obj \ + snd_sn76489.obj snd_ssi2001.obj \ + snd_wss.obj \ + snd_ym7128.obj + +VIDOBJ := video.obj \ + video_dev.obj \ + vid_cga.obj vid_cga_comp.obj \ + vid_cga_compaq.obj \ + vid_mda.obj \ + vid_hercules.obj vid_herculesplus.obj vid_incolor.obj \ + vid_colorplus.obj \ + vid_genius.obj \ + vid_sigma.obj \ + vid_wy700.obj \ + vid_ega.obj vid_ega_render.obj \ + vid_svga.obj vid_svga_render.obj \ + vid_vga.obj \ + vid_ati_eeprom.obj \ + vid_ati18800.obj vid_ati28800.obj \ + vid_ati_mach64.obj vid_ati68860_ramdac.obj \ + vid_bt48x_ramdac.obj vid_icd2061.obj vid_ics2595.obj \ + vid_cl54xx.obj \ + vid_et4000.obj vid_sc1502x_ramdac.obj \ + vid_et4000w32.obj vid_stg_ramdac.obj \ + vid_oak_oti.obj \ + vid_paradise.obj \ + vid_ti_cf62011.obj \ + vid_tvga.obj \ + vid_tgui9440.obj vid_tkd8001_ramdac.obj \ + vid_s3.obj vid_s3_virge.obj \ + vid_sdac_ramdac.obj \ + vid_voodoo.obj + +PLATOBJ := win.obj \ + win_lang.obj win_opendir.obj win_dynld.obj win_thread.obj \ + win_cdrom.obj win_keyboard.obj win_mouse.obj \ + win_joystick.obj win_midi.obj +ifeq ($(CRASHDUMP), y) +PLATOBJ += win_crashdump.obj +endif + + +OBJ := $(MAINOBJ) $(CPUOBJ) $(MCHOBJ) $(SYSOBJ) $(DEVOBJ) \ + $(INTELOBJ) $(FDDOBJ) $(CDROMOBJ) $(ZIPOBJ) $(HDDOBJ) \ + $(USBOBJ) $(NETOBJ) $(SCSIOBJ) $(SNDOBJ) $(VIDOBJ) \ + $(UIOBJ) $(PLATOBJ) $(MISCOBJ) $(DEVBROBJ) +ifdef EXOBJ +OBJ += $(EXOBJ) +endif + + +# Build module rules. +ifeq ($(AUTODEP), y) +%.obj: %.c + @$(CC) $(CFLAGS) -Fo$@ -c $< + @$(MCPP) $(OPTS) $(DEPS) $< + +%.obj: %.cpp + @$(CPP) $(CXXFLAGS) -Fo$@ -c $< + @$(MCPP) $(OPTS) $(DEPS) $< +else +%.obj: %.c + @$(CC) $(CFLAGS) -Fo$@ -c $< + +%.obj: %.cpp + @$(CPP) $(CXXFLAGS) -Fo$@ -c $< + +%.d: %.c $(wildcard $*.d) + $(MCPP) $(OPTS) $(DEPS) $< >NUL + +%.d: %.cpp $(wildcard $*.d) + $(MCPP) $(OPTS) $(DEPS) $< >NUL +endif + + +all: $(PREBUILD) $(PROG).exe $(NETIF).exe $(POSTBUILD) + +# +# This rule creates a script (command file) that figures out which +# language we want to make (its argument is the 2-letter language +# name, or none) by looking it up in the database of supported ones, +# and then it calls the (this) Makefile back with the proper args +# for that target. +# +# We do it this way so it can also be used by the VC project files. +# +win/Mklang.cmd: win/msvc/Makefile.VC + @echo @SET PATH=$(PATH) >>win\Mklang.cmd + @echo @SETLOCAL >>win\Mklang.cmd + @echo @SET LANG=none >>win\Mklang.cmd + @echo @FOR /F "eol=# tokens=1,2,3,4* delims=, " %%%%i in (ui\lang\VARCem.lang) do @if "%%1"=="%%%%i" set LANG=%%%%i LANGID=%%%%j PRILANG=%%%%k SUBLANG=%%%%l >>win\Mklang.cmd + @echo @IF NOT "%%LANG%%"=="none" @$(MAKE) -fwin/msvc/Makefile.VC "RFLAGS=$(RFLAGS) $(EXTRAS)" LANG=%%LANG%% VARCem-%%1.dll >>win\Mklang.cmd + + +langs: win/Mklang.cmd +ifdef LANG + @echo Generating localization $(LANG) .. +else + @echo Generating localizations.. + @FOR /F "eol=# tokens=1 delims=, " %%i in (ui\lang\VARCem.lang) do @win\Mklang %%i +endif + @-del win\Mklang.cmd + +win/$(RESDLL).rc: VARCem.rpp VARCem-common.rc + @$(PREPROC) -DPREAMBLE= -DLANG=$(LANG) -DLANGID=$(LANGID) -DPRILANG=$(PRILANG) -DSUBLANG=$(SUBLANG) $< >win\$(RESDLL).rc 2>NUL + +$(RESDLL).res: win/$(RESDLL).rc VARCem-common.rc + @echo Processing $(RESDLL).rc .. + @$(WINDRES) $(RFLAGS) -fo$@ $< + +$(RESDLL).dll: $(RESDLL).res + @echo Linking $(RESDLL).dll .. + @$(LINK) /DLL /NOENTRY $(LDFLAGS) -OUT:$@ $(RESDLL).res + @-del win\$(RESDLL).rc + +VARCem.res: win/VARCem.rc win/VARCem.mpp + @echo Generating Manifest file.. + @del win\VARCem.manifest 2>NUL + @$(PREPROC) win\VARCem.mpp >win\VARCem.manifest 2>NUL + @echo Processing $< + @$(WINDRES) $(RFLAGS) $(EXTRAS) -fo$@ $< + +$(PROG).exe: $(OBJ) VARCem.res + @echo Linking $(PROG).exe .. + @$(LINK) $(LDFLAGS) $(LOPTS_W) -OUT:$(PROG).exe \ + $(OBJ) VARCem.res $(LIBS) + +pcap_if.res: pcap_if.rc + @echo Processing $< + @$(WINDRES) $(RFLAGS) -fo$@ $< + +$(NETIF).exe: pcap_if.obj win_dynld.obj pcap_if.res + @echo Linking $(NETIF).exe .. + @$(LINK) $(LDFLAGS) $(LOPTS_C) -OUT:$@ \ + pcap_if.obj win_dynld.obj pcap_if.res + +clean: + @echo Cleaning objects.. + @-del *.obj 2>NUL + @-del *.res 2>NUL + +clobber: Mklang.cmd clean + @echo Cleaning executables.. + @-del *.d 2>NUL + @-del *.exe 2>NUL +ifeq ($(DEBUG), y) + @-del *.ilk 2>NUL + @-del *.pdb 2>NUL +endif +ifeq ($(PROFILER), y) + @-del *.map 2>NUL +endif + @-del win\*.manifest 2>NUL + @-del *.dll 2>NUL + @-del win\Mklang.cmd +# @del $(DEPFILE) 2>NUL + +ifneq ($(AUTODEP), y) +depclean: + @del $(DEPFILE) 2>NUL + @echo Creating dependencies.. + @echo # Run "make depends" to re-create this file. >$(DEPFILE) + +depends: DEPOBJ=$(OBJ:%.obj=%.d) +depends: depclean $(OBJ:%.obj=%.d) + @-cat $(DEPOBJ) >>$(DEPFILE) + @del $(DEPOBJ) + +$(DEPFILE): +endif + + +# Module dependencies. +ifeq ($(AUTODEP), y) +#-include $(OBJ:%.obj=%.d) (better, but sloooowwwww) +-include *.d +else +include $(wildcard $(DEPFILE)) +endif + + +# End of Makefile.VC. diff --git a/src/win/msvc/vc14/VARCem.sln b/src/win/msvc/vc14/VARCem.sln new file mode 100644 index 0000000..3c9f684 --- /dev/null +++ b/src/win/msvc/vc14/VARCem.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VARCem", "VARCem.vcxproj", "{6E445F28-CA8F-430F-8CCF-C59C53516AC5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x64.ActiveCfg = Debug|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x64.Build.0 = Debug|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x86.ActiveCfg = Debug|Win32 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x86.Build.0 = Debug|Win32 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x64.ActiveCfg = Release|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x64.Build.0 = Release|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x86.ActiveCfg = Release|Win32 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {85A5962B-0218-472C-A361-6E55D8FE7E9B} + EndGlobalSection +EndGlobal diff --git a/src/win/msvc/vc14/VARCem.vcxproj b/src/win/msvc/vc14/VARCem.vcxproj new file mode 100644 index 0000000..c334665 --- /dev/null +++ b/src/win/msvc/vc14/VARCem.vcxproj @@ -0,0 +1,774 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + _MSC_VER + _MSC_VER + + + + + Document + Generating Manifest file... + Generating Manifest file... + Generating Manifest file... + Generating Manifest file... + VARCem.manifest + VARCem.manifest + VARCem.manifest + VARCem.manifest + VARCem.rc + VARCem.rc + VARCem.rc + VARCem.rc + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + + + + 14.0 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5} + Win32Proj + VARCem + 8.1 + + + + Application + true + v140 + NotSet + + + Application + false + v140 + true + NotSet + + + Application + true + v140 + NotSet + + + Application + false + v140 + true + NotSet + + + + + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)\..\Lib\x86;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) + false + + + true + $(ProjectDir)\..\Lib\x64;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + false + + + false + $(ProjectDir)\..\Lib\x86;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) + false + + + false + $(ProjectDir)\..\Lib\x64;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + Debug + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + SKIP_MANIFEST + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + Debug + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + Debug + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + SKIP_MANIFEST + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + Debug + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + + + + + + diff --git a/src/win/msvc/vc14/VARCem.vcxproj.filters b/src/win/msvc/vc14/VARCem.vcxproj.filters new file mode 100644 index 0000000..cc44658 --- /dev/null +++ b/src/win/msvc/vc14/VARCem.vcxproj.filters @@ -0,0 +1,1724 @@ + + + + + + + + + + + + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + + ui + + + ui + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\input\game + + + devices\input\game + + + devices\input\game + + + devices\input\game + + + devices\input\game + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network + + + devices\network + + + devices\network + + + devices\network + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\munt\c_interface + + + devices\sound\munt\sha1 + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + devices\video + + + devices\misc + + + ui + + + + devices\network\slirp + + + win + + + win + + + ui + + + win + + + + + + + + + + + + + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + win + + + win + + + win + + + + ui + + + ui + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\input\game + + + devices\input + + + devices\input + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network + + + devices\network + + + devices\network + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\sio + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\munt\c_interface + + + devices\sound\munt\c_interface + + + devices\sound\munt\c_interface + + + devices\sound\munt\sha1 + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + machines + + + machines + + + machines + + + machines + + + devices\misc + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + + + {7a5514af-a83b-483a-bfac-af1e20215c22} + + + {cb655d08-c712-4197-9eb8-2f5b7f49eed1} + + + {7cec753e-1e2c-4ebd-ba5b-e728767c949f} + + + {e97ddf22-9ad1-46da-bc17-bd998507adf8} + + + {33d139d2-8501-4ec5-adf5-30695fd66b58} + + + {0eb40b0a-0704-4966-b094-dc23e7fb1224} + + + {a9a86e4f-b4eb-4687-a979-f97b394db706} + + + {98d7b877-02b8-4c6b-b2b6-d2cc49f357bc} + + + {923338ef-338a-4592-9839-9f8587d93c52} + + + {fc7fc92b-b4da-4f3c-a5c7-4d2548b60fb2} + + + {7cae45b9-af45-4f82-9c53-a4e3094ddee4} + + + {c0afa9df-08d3-40a6-a4d4-88fb97816009} + + + {b9d0388f-b14d-4448-85fe-7179fff3f77f} + + + {515f18e5-5eab-42f6-8364-37f02bc4f964} + + + {4fc4a59c-bb7c-45d7-a467-a06198e71472} + + + {2617b8d7-3afc-4f23-a0c8-936dab6c38b6} + + + {b28a24be-02cf-4896-9c96-aee25851df13} + + + {b8b30a24-da56-40f2-9a15-9c90ec2a98ed} + + + {144b7265-aa9e-453b-9855-78cd74ef9b76} + + + {309c083e-3028-4e46-a758-a8afeb95e914} + + + {4b343ed5-4818-4b62-beac-fcbc4185383f} + + + {99b35c64-342c-4f15-89b0-47c15f8db344} + + + {c21f8b49-308c-4e25-b69d-b03c1e24617a} + + + {51f1c4aa-4a2a-4639-bcbc-2075ffaf3e27} + + + + + win + + + + + win + + + \ No newline at end of file diff --git a/src/win/msvc/vc14/VARCem.vcxproj.user b/src/win/msvc/vc14/VARCem.vcxproj.user new file mode 100644 index 0000000..a1392fb --- /dev/null +++ b/src/win/msvc/vc14/VARCem.vcxproj.user @@ -0,0 +1,19 @@ + + + + -L varcem.log + WindowsLocalDebugger + + + -L varcem.log + WindowsLocalDebugger + + + -L varcem.log + WindowsLocalDebugger + + + -L varcem.log + WindowsLocalDebugger + + diff --git a/src/win/msvc/vc14/global.props b/src/win/msvc/vc14/global.props new file mode 100644 index 0000000..f19a0ec --- /dev/null +++ b/src/win/msvc/vc14/global.props @@ -0,0 +1,14 @@ + + + + + + $(ProjectDir)\..\Include;$(ProjectDir)\..\..\mingw\include;$(ProjectDir)\..\..\mingw\include\pcap;P:\SDL\include\msvc;$(IncludePath) + + + + _USE_MATH_DEFINES;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;USE_DYNAREC;USE_OPENAL;USE_SDL + + + + diff --git a/src/win/msvc/vc15/VARCem.sln b/src/win/msvc/vc15/VARCem.sln new file mode 100644 index 0000000..6036e55 --- /dev/null +++ b/src/win/msvc/vc15/VARCem.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VARCem", "VARCem.vcxproj", "{6E445F28-CA8F-430F-8CCF-C59C53516AC5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x64.ActiveCfg = Debug|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x64.Build.0 = Debug|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x86.ActiveCfg = Debug|Win32 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Debug|x86.Build.0 = Debug|Win32 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x64.ActiveCfg = Release|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x64.Build.0 = Release|x64 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x86.ActiveCfg = Release|Win32 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {85A5962B-0218-472C-A361-6E55D8FE7E9B} + EndGlobalSection +EndGlobal diff --git a/src/win/msvc/vc15/VARCem.vcxproj b/src/win/msvc/vc15/VARCem.vcxproj new file mode 100644 index 0000000..1343081 --- /dev/null +++ b/src/win/msvc/vc15/VARCem.vcxproj @@ -0,0 +1,783 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + _MSC_VER + _MSC_VER + + + + + Document + Generating Manifest file... + Generating Manifest file... + Generating Manifest file... + Generating Manifest file... + VARCem.manifest + VARCem.manifest + VARCem.manifest + VARCem.manifest + VARCem.rc + VARCem.rc + VARCem.rc + VARCem.rc + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + cl -nologo -EP %(FullPath) > $(SolutionDir)..\..\%(Outputs) + + + + 15.0 + {6E445F28-CA8F-430F-8CCF-C59C53516AC5} + Win32Proj + VARCem + 8.1 + + + + Application + true + v141 + NotSet + + + Application + false + v141 + true + NotSet + + + Application + true + v141 + NotSet + + + Application + false + v141 + true + NotSet + + + + + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)\..\Lib\x86;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) + false + + + true + $(ProjectDir)\..\Lib\x64;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + false + + + false + $(ProjectDir)\..\Lib\x86;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) + false + + + false + $(ProjectDir)\..\Lib\x64;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + DebugFull + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + SKIP_MANIFEST + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + DebugFull + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + DebugFull + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + SKIP_MANIFEST + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + DebugFull + winmm.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;iphlpapi.lib;psapi.lib;Comctl32.lib;DInput8.lib;d3d9.lib;d3dx9.lib;ddraw.lib;dxguid.lib;version.lib;%(AdditionalDependencies) + 5.01 + + + $(ProjectDir)\..\..\..\..\src;%(AdditionalIncludeDirectories) + + + + + + \ No newline at end of file diff --git a/src/win/msvc/vc15/VARCem.vcxproj.filters b/src/win/msvc/vc15/VARCem.vcxproj.filters new file mode 100644 index 0000000..0dad9d4 --- /dev/null +++ b/src/win/msvc/vc15/VARCem.vcxproj.filters @@ -0,0 +1,1750 @@ + + + + + + + + + + + + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + + ui + + + ui + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\input\game + + + devices\input\game + + + devices\input\game + + + devices\input\game + + + devices\input\game + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\input + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network + + + devices\network + + + devices\network + + + devices\network + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sio + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\munt\c_interface + + + devices\sound\munt\sha1 + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + machines + + + devices\video + + + devices\misc + + + ui + + + + devices\network\slirp + + + win + + + win + + + ui + + + win + + + devices\cdrom + + + devices\misc + + + devices\misc + + + devices\network + + + devices\network + + + devices\network + + + devices\printer + + + devices\printer + + + devices\printer + + + devices\video + + + devices\video + + + machines + + + ui + + + ui + + + + ui + + + + + + + + + + + + + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + cpu + + + win + + + win + + + + ui + + + ui + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\cdrom + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\disk + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy\lzf + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\floppy + + + devices\input\game + + + devices\input + + + devices\input + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network\slirp + + + devices\network + + + devices\network + + + devices\network + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\ports + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\scsi + + + devices\sio + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\resid-fp + + + devices\sound\munt\c_interface + + + devices\sound\munt\c_interface + + + devices\sound\munt\c_interface + + + devices\sound\munt\sha1 + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\sound\munt + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\system + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + devices\video + + + machines + + + machines + + + machines + + + machines + + + devices\misc + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + win + + + devices\misc + + + devices\misc + + + devices\network + + + devices\network + + + devices\network + + + devices\printer + + + devices\video + + + + + + {7a5514af-a83b-483a-bfac-af1e20215c22} + + + {cb655d08-c712-4197-9eb8-2f5b7f49eed1} + + + {7cec753e-1e2c-4ebd-ba5b-e728767c949f} + + + {e97ddf22-9ad1-46da-bc17-bd998507adf8} + + + {33d139d2-8501-4ec5-adf5-30695fd66b58} + + + {0eb40b0a-0704-4966-b094-dc23e7fb1224} + + + {a9a86e4f-b4eb-4687-a979-f97b394db706} + + + {98d7b877-02b8-4c6b-b2b6-d2cc49f357bc} + + + {923338ef-338a-4592-9839-9f8587d93c52} + + + {fc7fc92b-b4da-4f3c-a5c7-4d2548b60fb2} + + + {7cae45b9-af45-4f82-9c53-a4e3094ddee4} + + + {c0afa9df-08d3-40a6-a4d4-88fb97816009} + + + {b9d0388f-b14d-4448-85fe-7179fff3f77f} + + + {515f18e5-5eab-42f6-8364-37f02bc4f964} + + + {4fc4a59c-bb7c-45d7-a467-a06198e71472} + + + {2617b8d7-3afc-4f23-a0c8-936dab6c38b6} + + + {b28a24be-02cf-4896-9c96-aee25851df13} + + + {b8b30a24-da56-40f2-9a15-9c90ec2a98ed} + + + {144b7265-aa9e-453b-9855-78cd74ef9b76} + + + {309c083e-3028-4e46-a758-a8afeb95e914} + + + {4b343ed5-4818-4b62-beac-fcbc4185383f} + + + {99b35c64-342c-4f15-89b0-47c15f8db344} + + + {c21f8b49-308c-4e25-b69d-b03c1e24617a} + + + {51f1c4aa-4a2a-4639-bcbc-2075ffaf3e27} + + + {4797388a-1c9d-4a4c-a7f9-aa1925cad334} + + + + + win + + + + + win + + + \ No newline at end of file diff --git a/src/win/msvc/vc15/VARCem.vcxproj.user b/src/win/msvc/vc15/VARCem.vcxproj.user new file mode 100644 index 0000000..e0a0433 --- /dev/null +++ b/src/win/msvc/vc15/VARCem.vcxproj.user @@ -0,0 +1,19 @@ + + + + -L varcem.log + WindowsLocalDebugger + + + -L varcem.log + WindowsLocalDebugger + + + -L varcem.log + WindowsLocalDebugger + + + -L varcem.log + WindowsLocalDebugger + + \ No newline at end of file diff --git a/src/win/msvc/vc15/global.props b/src/win/msvc/vc15/global.props new file mode 100644 index 0000000..1c090df --- /dev/null +++ b/src/win/msvc/vc15/global.props @@ -0,0 +1,14 @@ + + + + + + $(ProjectDir)\..\Include;$(ProjectDir)\..\..\mingw\include;$(ProjectDir)\..\..\mingw\include\pcap;$(IncludePath) + + + + _USE_MATH_DEFINES;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CRT_STDIO_ISO_WIDE_SPECIFIERS;USE_DYNAREC;USE_OPENAL;USE_SDL;USE_FLUIDSYNTH + + + + \ No newline at end of file