From b3dee48c9ccf35f3231d41057a8d3faceca784f3 Mon Sep 17 00:00:00 2001 From: waltje Date: Sun, 4 Nov 2018 17:37:57 -0500 Subject: [PATCH] Initial upload of modified SLiRP --- .gitattributes | 37 + .travis-build.sh | 82 + .travis-deploy.sh | 86 ++ .travis.yml | 91 ++ COPYRIGHT.txt | 61 + README.md | 44 + git | 0 src/arp.c | 75 + src/arp.h | 42 + src/bootp.c | 359 +++++ src/bootp.h | 122 ++ src/cksum.c | 145 ++ src/config.h | 71 + src/debug.c | 90 ++ src/debug.h | 70 + src/host.h | 14 + src/icmp_var.h | 65 + src/if.c | 250 ++++ src/if.h | 44 + src/ip.h | 365 +++++ src/ip_icmp.c | 378 +++++ src/ip_icmp.h | 170 +++ src/ip_input.c | 704 +++++++++ src/ip_output.c | 220 +++ src/mbuf.c | 245 +++ src/mbuf.h | 146 ++ src/misc.c | 135 ++ src/misc.h | 74 + src/poll.c | 342 +++++ src/queue.c | 115 ++ src/queue.h | 101 ++ src/sbuf.c | 204 +++ src/slirp.c | 294 ++++ src/slirp.h | 133 ++ src/socket.c | 769 ++++++++++ src/socket.h | 130 ++ src/stats.c | 225 +++ src/tcp.h | 213 +++ src/tcp_input.c | 1735 +++++++++++++++++++++ src/tcp_output.c | 618 ++++++++ src/tcp_subr.c | 714 +++++++++ src/tcp_timer.c | 337 +++++ src/tcp_timer.h | 138 ++ src/tcp_var.h | 256 ++++ src/tcpip.h | 70 + src/tftp.h | 1 + src/udp.c | 452 ++++++ src/udp.h | 117 ++ src/unix/Makefile.linux | 271 ++++ src/unix/host.c | 81 + src/unused/main.h | 21 + src/unused/misc.c | 128 ++ src/unused/redir.c | 65 + src/unused/redir.h | 12 + src/unused/tcp_emu.c | 654 ++++++++ src/unused/tftp.h | 40 + src/unused/udp_emu.c | 251 ++++ src/unused/udp_tftp.c | 332 ++++ src/unused/x | 653 ++++++++ src/win/host.c | 121 ++ src/win/host.d | 1 + src/win/icons/varcem.ico | Bin 0 -> 64638 bytes src/win/libslirp.rc | 84 ++ src/win/mingw/Makefile.MinGW | 312 ++++ src/win/msvc/Include/d3dx9tex.h | 300 ++++ src/win/msvc/Lib/x64/d3dx9.lib | Bin 0 -> 81324 bytes src/win/msvc/Lib/x86/d3dx9.lib | Bin 0 -> 87600 bytes src/win/msvc/Makefile.VC | 854 +++++++++++ src/win/msvc/vc14/VARCem.sln | 31 + src/win/msvc/vc14/VARCem.vcxproj | 774 ++++++++++ src/win/msvc/vc14/VARCem.vcxproj.filters | 1724 +++++++++++++++++++++ src/win/msvc/vc14/VARCem.vcxproj.user | 19 + src/win/msvc/vc14/global.props | 14 + src/win/msvc/vc15/VARCem.sln | 31 + src/win/msvc/vc15/VARCem.vcxproj | 783 ++++++++++ src/win/msvc/vc15/VARCem.vcxproj.filters | 1750 ++++++++++++++++++++++ src/win/msvc/vc15/VARCem.vcxproj.user | 19 + src/win/msvc/vc15/global.props | 14 + 78 files changed, 20488 insertions(+) create mode 100644 .gitattributes create mode 100644 .travis-build.sh create mode 100644 .travis-deploy.sh create mode 100644 .travis.yml create mode 100644 COPYRIGHT.txt create mode 100644 README.md create mode 100644 git create mode 100644 src/arp.c create mode 100644 src/arp.h create mode 100644 src/bootp.c create mode 100644 src/bootp.h create mode 100644 src/cksum.c create mode 100644 src/config.h create mode 100644 src/debug.c create mode 100644 src/debug.h create mode 100644 src/host.h create mode 100644 src/icmp_var.h create mode 100644 src/if.c create mode 100644 src/if.h create mode 100644 src/ip.h create mode 100644 src/ip_icmp.c create mode 100644 src/ip_icmp.h create mode 100644 src/ip_input.c create mode 100644 src/ip_output.c create mode 100644 src/mbuf.c create mode 100644 src/mbuf.h create mode 100644 src/misc.c create mode 100644 src/misc.h create mode 100644 src/poll.c create mode 100644 src/queue.c create mode 100644 src/queue.h create mode 100644 src/sbuf.c create mode 100644 src/slirp.c create mode 100644 src/slirp.h create mode 100644 src/socket.c create mode 100644 src/socket.h create mode 100644 src/stats.c create mode 100644 src/tcp.h create mode 100644 src/tcp_input.c create mode 100644 src/tcp_output.c create mode 100644 src/tcp_subr.c create mode 100644 src/tcp_timer.c create mode 100644 src/tcp_timer.h create mode 100644 src/tcp_var.h create mode 100644 src/tcpip.h create mode 100644 src/tftp.h create mode 100644 src/udp.c create mode 100644 src/udp.h create mode 100644 src/unix/Makefile.linux create mode 100644 src/unix/host.c create mode 100644 src/unused/main.h create mode 100644 src/unused/misc.c create mode 100644 src/unused/redir.c create mode 100644 src/unused/redir.h create mode 100644 src/unused/tcp_emu.c create mode 100644 src/unused/tftp.h create mode 100644 src/unused/udp_emu.c create mode 100644 src/unused/udp_tftp.c create mode 100644 src/unused/x create mode 100644 src/win/host.c create mode 100644 src/win/host.d create mode 100644 src/win/icons/varcem.ico create mode 100644 src/win/libslirp.rc create mode 100644 src/win/mingw/Makefile.MinGW create mode 100644 src/win/msvc/Include/d3dx9tex.h create mode 100644 src/win/msvc/Lib/x64/d3dx9.lib create mode 100644 src/win/msvc/Lib/x86/d3dx9.lib create mode 100644 src/win/msvc/Makefile.VC create mode 100644 src/win/msvc/vc14/VARCem.sln create mode 100644 src/win/msvc/vc14/VARCem.vcxproj create mode 100644 src/win/msvc/vc14/VARCem.vcxproj.filters create mode 100644 src/win/msvc/vc14/VARCem.vcxproj.user create mode 100644 src/win/msvc/vc14/global.props create mode 100644 src/win/msvc/vc15/VARCem.sln create mode 100644 src/win/msvc/vc15/VARCem.vcxproj create mode 100644 src/win/msvc/vc15/VARCem.vcxproj.filters create mode 100644 src/win/msvc/vc15/VARCem.vcxproj.user create mode 100644 src/win/msvc/vc15/global.props 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 0000000000000000000000000000000000000000..97f7f1710777ee465f2250cb355ba36e90533d50 GIT binary patch literal 64638 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

swRi^ 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

>9cT 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8587d5b07ffc2e3cb09e930a446908fc5b8c0651 GIT binary patch literal 81324 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 literal 0 HcmV?d00001 diff --git a/src/win/msvc/Lib/x86/d3dx9.lib b/src/win/msvc/Lib/x86/d3dx9.lib new file mode 100644 index 0000000000000000000000000000000000000000..11853bf0acdaa8f9014a2f665c08ba453f32bf9c GIT binary patch literal 87600 zcmY$iNi0gvu;bEKKm~?I<|ZbVhK3gAsNx1tu92~!u?d)+z{SA8G?js2s~ZEu_7Vn$ z9cvgEK3#+0y)PITK=@7{0|OZUV_>-RkAdNl5(MwN$H1`f9s{FW4;VB2eZ&C5jE;X8 zK$zjM1_Kx$+Q7g7!pFoI7{IuTf#Dbk|6^b{_K$(#co_trieq2^;jan|3||!(7&)9E zm~$NiBM5VBV_*bfP9p|J5VpF-zzD+DJ`9Z3J`4}f#5Hz7#O~+Vqj!@1;Ol542)oWfq{|z0t2I~0R+2= zF))I#>ks0tV}Iz3^4;5-IKr#uFRJtbhw7`l%E zgc(k}V}Rf+28I(^3=9vPz?jj{i2;mV_AxMmu^R)UiyH%@^BD-{6JTHjVYfdFjBbAz z815E9@ZT&3hQC=1j9fDyn0pojBN&S@Fmj7AFk0S&V5=AgMynVGhTjSh{7Zv@0fc`~ zVqgH_KUEA2V9db4@P~nc;nzF}zOBZ<0K%s(Fff4d$vg%I5dOc3fdPd7onT-9<1hw> ze_;%as&Npk@``~`U_{|yy1`vMof`I{qpGGk- zfbkgyhNov37@m|t@J&Ak1`xirg@FNtZysP^0O4C!3=AOra2W#w80#=FJk()exV-{` zZ`@#D0O2bS7#KkKS_A_F7++yvxORns;YI`mU(8@&0O6}K3=Cj=i-Flhe8*lrmEBN(eOFxsgxFbd^Cuy6(gBM6JcFffAgEe1xBTMUdsFCbVnf`Ji)g$ zg0Mp$10x7KwlFY)F&hJ;BO3$5-Yf{-^MZi^g!e@;Fo5tgI|c?2J{`os0K#wO7#KkK z*(L@C5PoUGzyQLhPcbln@VNvA1`vKbgMk5rKUgp@fbh!=3=AOr+Ju1tgwH)-U;yC@ z1q=)z{9zXZ0|ug!@%%<4FjXS90WUvF))I#{WJzf`)LdeKLQ~5TMYvP2!EZxzyQME6Brmk z_}e=M1~6u0VED$y!0`PB1Rq_%zyQKOH!(1Pu?+*mPa6hCk26pl#lYwh#lYzP42laF z7~KmP7(MME*h`Cn5rn-J7#Km=w}gQajF}i1eVG^-efpqSf`QRTf`QTR90dFCVqgT} zfDH_cAnf~$ff0lQS1>SwaBv+1BM65iFff8}@H+-ZFy>-l4CZ2B44MMLA(t2!LD+K> z10x7~En;8tfa)i2*S$$7#KlV!Ht0tjCV0GD(qrl zR9pwa@&OEtV0?msQT_x2qoN)JE4MH(f-wgJqcR5rqgV(8i(X=21YwC442)oG!oVnD z!oVn(0>LuR7#P7gi-A!li-A%49R$lhVqgU01O`Ug1O`UwDhQV1V_*d1e+-OLAl$^j zDAmNks5A|NPst6pJX1YtEl z21XE8-@w2K!Wt?Jj2bEo4Es+&u^$7&em@3=KffUOjsyb(2=6LkU;yJ!3=F$IF)-{d zfMO6EB*wtNu#16#;lK_EK4`+g0LB{_7!HEk5(W$mhYT1P4!D3ZBNM3XoX5b(mIuZR zKaMbfFe7Ul0|+yGF=7DY&$}2HKJQ{+Wc>lbEJ6&7U_60=k!1n{BP$;SGfOZqg7Fjv zM&>CD3~x*z_`e+k!+$#lMq?HTwwT1g2*Q>O42&RbxQ&4kge?mg7(v)biGdM>P2Mmt zg0N{E10xt;U|=-8z`$tc1jSnz7|pgYFdA1uut^aEBM2MyF))I#@izuW5VnwEU<6_F z1q_T}tj54-uExM{F9nKkF)-Y_#lUbs2!da4VPF8^`)3#!K=}A428QFG7#M90AlPOT z10x9YPGDdJV*v(6UI7M18w&`w-p9ZQ!aP$L7{OSAfssdofl*Tgil;CzYEEHb)DnT> z84QeCGZ+{(=7Dg0yr*w~e^5xgi)*lRkY_-Me^7jiaY}_{yooVGe7s+%j}N8_hI*J1 zh=LH0P(SZ@7sn7shIkiamk5{Cq|$WX(vnmM6NdQs%-n)_Okt2Z=Zw_k?6B0L;>`R! z2Mc7S=rUl{MX8A;slKVj84gA$<~pNFpsIBXbHk||CWp`w8~`!j0L3w2^O2;$>T^;P z^B{^*9fKkcQW=(*lbHfmX=sFOEUGw2rF&{#YEfcID%24uA%ZCf(h;1QTac5PRtYxV z2t@;i6iB^uer`c&NvczRXVA4e(f*?NP7M9i*gfl ziqT>mVk?pyl8%tXy!6z(67-0KXhD|5)ZD;&&FV;IF+uy&{# zunFMA;#8WJmRf{nEt(8SwOeLRDkS-$W))O%kV>EYL~tTqQan5YIIs{xr1T#%Dl0?IOIr6{U6L?x&?23O7jMfpjo zXjvOlTcb&X^f~2Mpotytbj9O(tG7YLMNLNs5atT;8 zII}9%0kv(0DF@OKQk02H1BMh>J-oz&Nu&7>QR1N}K-CL(3r@|bDnPnHt$(jgW}kRC`lL&|oLwWvicHd&A^Si9E0w4|W41XNI?>ccJ% z(h2ea$R3Z()FRLTdnMSP=nV#Zs&JbEX)7Rd5q47`s&JcvXc}Q{t)V&`MKQ=sSStvw zEF>S)EObOm26&a>F(;fjb0Eq<=73weE~&*OMfsJeafB`dSM69(keZj`oROK6f<*_K zEL@jcW*#=>Fk!g5pw#rt;*!)Nzr@_s6yL;>B5-MjQka2VhEpBfB)`&JR6|fIaj+rS zl)&{nC*~xVf?77Fl=HOEbG7pkZyi+UFKpmG9$GptkL{R-5oLYj`P#~ld zVH&7@Dgu>7gpI?a6k#6RQO@~!B}Mr;IjKcxi3#F1T~vEI^V%s1M06D#d6EfNN!h43g^9#De@1v?>>3dI&-aS$#zbdY2cb z943RR8q%Ldl-~%|5E+E(;DVye67&HAu*(rL2-R-+c_oexCMbyxVm3ksRdqN{)esp} z)t-4NnMJ9|C5~7F2V@7T3hcVW3F?NZK!>< zrs7Ma*i>NG4M~3pPhgr4QGux&mSWIrUQ`oMRpB-TlJ0Q117-?D6{aZ|Ne-7m7z#1X zf`lEGlnIdl1v`WVR)VP?mgG=6MW}&-qy(fN6zdQpaVJVld2Bi%DGuRzbdw?S*mR;K zG~5QDC%~-o z&<%ABD8+)(J@(EVRz*0?fK z5XA^Hk>VAW0zq+$%|tv(377}56g}->H4mZ`VIErKA-fS~Qo2ScJ@mXh4{a6d9nD3U5ncTYrO3Ey6^!AR}lhA@vB8 zF@lVs*@Sfpe8}c0#Q_HkUqDZ{KOJtqmX<3S@h&}Ct|LW&aeiqrCoa?t_}WD%+yh7RZa z{Gt?m+7Jpb^g@CUpJpT#P~FB5A#7oeDg{%I9I}X{88uFCMZ%c^~iCI5?Ch4GBDL>5sazL2{42CpJA3PV}(uqX@R4*cR zK=fjy18fRFdckYBDqK=aKy^@FVqOXA1V2`3WPQo`xdr*f=*wnd+K^=ty1;1`tP7=n zglIyMMCkD?%_+$&$U(2aA$m|GG4&vfM4c)_)r_hNVTvbcJuJwFC|!4mUSv6hj^O0P zoXos*w2}*=16dBC18jE=c+nYFUFh-%ok95}U~OpaHi%^?l9+lTQQLZ`dccyHdZO^@ z0ZU@)ajeKJMk~-z?Sd=7)EiiuSdvX0Zx+Zx zB29xTMVRN4pP%hm66Aq3d{HFP_4we^1C>PR2}ms}E=Wx-$t+6^@<3a?2k|_n3aq+) z2bGIT|_%m7(|#mA^-Koud( z2+l}M$uGxRz9C5=)CZ-e<$zkvSZqR)0;xxoY9aZcBGM7GX9LY5+-guwfE93%mtr$kwfRIJgRalysf;KvU zqzf*KMOScYNh$jJQ#7pzC8+w*dqUVE4_yt&gn*pHJm_c;M$}=-!F9lQ`hnNDq7L1G zwP7lN>jh0Y2IObvl|T{%TKWg;#-;>QKYE`QRVzdmt_xn-W3vxc5>pSh&H&gpxJHBu zkZ$Mvoctokl&sR?lHkOW(jrJx2es9VTMe2C;GGJI#Tbju5q6-c0O@usN-fSy%?U{@ z%0*4_Xc8c`kf|<6c@|ufn4FEO1FJMhpJQ=xYHm_aDyVSAz2Xy}D%_?(R&FBFFm_WQ zs<4{^o8CcR*@8=Rk8fZ||Og?P=vSg4H4EQmr3v+y|uEC~%+3EJPA zk(iQN6i}3(mYIXOaR<8+sD99fn$!wNa>r!=4mBVXKzqO-nw|4=LF=8+Y8LGBAf4{1 zB@m6^-JU4p^%znh^)8vki71&9%_i*fXgVRQiqQ&7B%Mg|Af0fBz|KU$?o1?E6kVQq z1*Ijyskw=HC7H?Som{xtSX7~y0@;X3&=_pWP|OKV%q_@CEyC#z6nPY#&iQ%8C5d?@ zA&E&jXnR87zQ9m}+XUANvhs#av&YS9jL5C)(r0qKY2{eYkl=n+1s&2ntASagM? zVw{QuUX}ni7N!72FUa3eTT!>_!)--VfzS;tirh;}N>YnFQgaG$?8wEX4q;M2P>5?@ zI%qQ<>I!+d8<1qdy7Kdq6H8L_pht717Nf7v#VQZd30m6#jY){FAuBMzdtVTZD}1W( zn1Z%R2Zt$ORUlJ9%fwOK0_r@0){LOJ2ERJICV}lj_zR~=V09pqpl&7{HW(^!=mwjN zZ~+$c!74zyp{|3Nj6W=}sKIFh*nETwu-XDv12Q3?D7By{KRLCy7*YkpoPmA-0)BP) zOoFV+N4N)=hAuuvh7e~)2A9ta46D8{FuY=8WQbyC zWVpc2$Y9{g$ROs%$gsqfkzvVU1_rew3=FG|FfgbbWng&X#K@56#K=&xhk;>VC?mtW z6ATPlCm0x<9xyOC88R~Lb75p~b75pS|8)?WsO8C;AEv$z=< z#JCw5?pZQ2#8@#hDEww%(D=o`FzGh~Llx*C=06M!^L{ZfsNH5@xNwSrA@3vu!>0cX z3@83EFogYMV2D#?WO${*$gtx91H-$M3=Cf`Ffgz@VPIJEhJoS569$H;rwj~do-#0$ zJz-$*yUDsl~ zfg$E91H-MW3=ARH85pizXJE*=!oU!6iGkt9B?g9=%M1)pE;BIPy2t=N=2(T1kzpPq zBZC|h_()?07Jfzs1_4HfZvu=AC4!6$b^MGBp9C2hsstDr*4Z*LEVE-|P_tuX$P;2@ z$Pi{^h!J6AxFy2K@Ir`@Awraq;gK*SL!Sd9LyIFL1DhiwL)Kmfh8KGn7^3zuFxWj~ zUjMKrz$XR=_vXelRf9d}Cmk@Rflf z;X4DvyKf8(Y~L6dZhU88Sa6hqVbf0r2AiJ@3}-wT8KOKG8J@W_G8DKoGT3=CGH7`* zGAMX6GL-l-GBEivGW7W{GD!F^GMw{cWZ31;$gm-Rk>QyyBg2Y7MuxgzMuvnCMuvC6 zj0{}Cj0{tP7#S{wFfwfNWMo+6#mF$rn~}lEpOL{RfRRBfkdYz6kC8zph>^i9l##(p zoRQ&*7$ZZO3L}GzBqPHFNk)bP;*1PRN{kHulo=V^6c`zHDKIjuQ)FZakY{8#ASLC z1_rmCC`(0vAmEw33E&fI5U_;j3s z!N8W0VUrCb!vtPN1_5411`8WThJDtI3{!X*86)AcHW2Cqpm8M25Kx^B7hztY(49N@`44Dim3<(U84ABfR3{eao4Dk&7jBE_-4Auyx3>*wH46+Pz48aT$4518R4B-qt4E+qV8Rjr7XIRBBfnh4c zLWV^Q(->?S)fht<`WPlN%x74@u##a7!xV;@3`-f7G0b9MVNha_XOv@9W)NZ6#ITiN zBf}Pk%?#TZHZaIA?qt}(;LPC8;Kbm<;Kty}z{hCAz|G*x;KQKK=*>{TP{L5iP{dHo zAjPP{sLH6wsKltuD9fn8D9xzCpvs`eAj%-lAi*HUAkSdSV9#L3;LhmA=*;NEXvv_! zP|8rrP{vTrP|i@nP{mNs(9F=l(8|!r(8SQf@QUFj!!3rJ4EGq`FuZ2C!*G}33&S^t zuMD3VJ}|szc*pRG;UmLah93-17@jgbWO&5zjNviEeTMH0FBqOPJYcxZ;LqU4z`@AL z$j->d$i>Lb=)>s2=*j5C=*`H=$jJDgfr0Ta!#{@K48IuuFfcJPGqN!LWH4pWX4GXg zWz=9aW;A0oVbo*PXEb0mWHe&5WVB#3XVhfWV$@-DU~puRWRPNTWprV5WOQJxVW?xM zWmwCwo?#tB8$$;}I|C1cK7$^EB7+5kIfEI4I)f&I1_Kv^6~kVJJq)`Ub}{T@xWKTV z;UdEUhD!`b7!ETWVmQcfg5es&QHIM5#~7|K9A~)7aFXFV!#Ret45t{*Fq~#M&v1jm zkb#kbX*5-irpiHIk=UPCq$B#j7t(D4FTO@JvuoykBt z)f#*RmI1;7B&lHVcouk}4$SP*l2qKsmts`}ou7alataS=$fAF!QA8giiev#~CKo(d z22H%6u!Zk40H2qQq#SyDHK_XrK3yHE7%B`tYYsGMjj01Hg|r988RrNMwo^Z`9luHV z&`0QU%34CGTB$Jv2>3sHpF(vFxBM3(`jI7oU04Z361 z3{wKyI0za6C7=j>t0Yq9hna-2g$LqyJSw4^sPI~izK0pjI9R5E>|@4h5=7eb6#3`ehnm+d&7%gAUpP&p{%r zfeC}c5;PlwMFz`>_(-atbv{bHg6(ie0%uPmOa!&K(DM{X40JL$ygdN+5>ynF)v(nw zXwslO2HN*2E3l3GFMgXjxgw46=bqbO(uq=;WpI}h}@)c;y zJyu`A%mMj~pd$1-1Zo+^tQUq&kO>6TRs^<#Nm2HDf{%Sc7=_W`Yv{aY_-%zya>i};;XIz7OAV?>P z!wm#2--6{*up+#tZWB1y8)fw-$O_y?$-`v8l`y=j0xj1`P0m3*85$}G>jPGS7brnY zFL6O#_yZNiIOZBEj{RUsT#|@vZv^Stq94=$EOJ((q1b?X85wM$ABu9=FbcG!aZfFQ#w7ZZMvyM}`fK=T2V4Yl z3kLX#6BL!mm$rbWw?XYfP-OwVvI(RXMHn8fu=Y2KEG*jK_M^xmnjk2`pi6n6X#^$? zzd``&6j zNA(s$9>Riq3eo|?wxbYZ4l zB#L4t(k?v$`~3)PDFitOm&v%@jAT7r5y*`wE=N)Xl0&ftZRZ*!B*4pJGxPIMHiDr_ zA$AGD+90U%;BC!_c!ZgVV{HgC&vnccYmKPI^$&uwPjL69v_r@XIkkrhs?zfC?M*ok^IIXd8>*^(uJC z1=auun+F*dhwiI_8wB|%0@UB2mh$5*6?FYoQsSka-Do88*!Yptb5mwFyd!R0MM_sb+T9%lDYpFEq z;(d?}kmb>Earn}0Xp0IWjeGSp$TIL;73!*Qh{>?1gDjp$6~ny{9#x$wJl+vY?va@NVD1-UJZCH>>cpDaG4>TuXk%PBAp>l}UDR#Z+ z@+kVDEjO4>SkVJA8bu1;M#HHB)#tFm1n9kCuwoTtE{YV~T%0NpLoOiAkTw}M=OW9% zO+|JsvH~Q>qF-YSjwbN9E%-()#BGQW3FHeEK_ZBI^dQ6Qpi2#*tx<#&Jk!G)fe1Ow z>-W&F`U6=5y#W(C^aHwu6G=1xwCmFZYAaMF=JlH3yYoOP5}yCSB8Y)^SQLQHHN#d1 zfW)yM^JIkOxG2=)-$1Sf>4(>n2uGwQ6Mr}pc3s%dF+!LMsa3G+g~*}S*4#yA4N z7<&LiSPG9; zh_kWlg~(xQhM&lU;xD);L<5q~OmGCR3HIQ{a?}}?6o>tcGpu2V>li4E8%Qy4peN#v zdZdfzQ5W!lo5P4j^{A3a4M3F5m)KQ7=8y521#99#=8uU{38|70Dlu0UA>_a-gFqE6 zv^d9DUxcOY!Evk^*oGFcn-~VHg5kXhhnT z%b*?`=(Q7cha#FkF)0 zxdBl30$SNr!l)~GPpk@&X z>Y*#9h|mFcA@gmTE-K9Np=w8$|OBd@ALmxNAy zq7{YUn1@J#k`jD{6?WY)B_NkW2THJ{R#f9)YSC*;NN`}Pg@p)uok>VNdd-Qf9=iSt zyQg3dgp7~lR|ASw+#v`v2{fBcSQTVI9k+dmfWU7SOdZH=XjKzZ?1J2eP>E4D;ZTWD zDpVZdzMza~F7Su#TZ2@ER4EAyW$w*kS23eSd$DJ^RAfJPln-QfHvMh@T zrP%9as5>!Mso^jXG~a{0rH5FthOQMlss_1l4ZLm*uL}H$1jR6zQd|LsLoxOs!>SmT z6tI?Ucy)jh50=IURz2905Qb7%`oNx$a4H9f4lz!FnGb3okaS@VdeTB!`G$yWtn1&9 zq_M7o!zvF=hN$gbtSjOW+Cc>mq|pUh7l$IAgVgE;k80tRg{)#jm4;>^EE=IQ*fgQU z78b22@>mQ&TS15HB5dpEkmRwgrbCm)a|;NTm32s(L9+PP*P$szT4jeO16yl{CX8*x z9hwrH>+X=%p{%|`NWfOwL5>dtC0EowEleKVEJAAxK$qSj*5e@?TLLl*HogH_m4_;g zx;76*4r7HLiY)eZdia$f3N@62+HtPg!)+R@#DJ{aBdiX+pu@R}55*`*ZpS!OjgT7X ziaspLu&nFDq6(4<2%a^Evc?aKG2mHTl;Rh@z7RH)i7pFGDd?;I5Qf87B%qBi!o`tR z0AkSvTL*}c0xgRIbpk*&Ds(j<4oP_8LGAUS7ywHaXs$z1gWiZlk%X=i#B>6DvK8u3 z&{{!4st_(lN$xNUQH@0Jrh`<$`!FC&LDdL&^&qOX7;3 zlpre)(L9G({|HltJ|KW*8q{*|f<08TAoVT2P(W3IClF8-VhaO=3Rn=JlmH+{Bb30r zhAjjzRbdMNgenLN&HIpH1xG9wCV-Z1p+pXDrD*d25QV5K7!d&ulf`?L3T(j;YF81W z6|#5{x~37`T!-h_Fc@$Ktn(kiU>@gewKvi^X?HN%4F&KrF&SZO1{P0cH$%d6!0RV*N)mfn3V1Cgx+$RLM6eZ=7;?C;N&qKL z(BeYyk>j8&iY0te6`?g%A>IcoL0V~v?gX$rI1`|l4_ja9mY57$$bu4cFgZ~B1-|wY zVJ>(PB&di*UxA5B31l56nk>AH10E(t?E->rfK2Z}7geBX273w=cxd?_tPw5=S&RT$ zHG!lZZK);NU>p9mnz)St1qZ0!g)BG1uMSZY;CC9xOqg2qX$?e{Kwn>hup6NUx{?!{ z6s&kgNgN2BkX4=7G{Op5v=~5W#Swo91+a_@U+0O<8kizTVn@yYm_}i&`NU=SJ*SOtpB0Q9w>2sQAM zTHHVugu-Q!*NdVXfo;_&x*G7x0o1jl$Vy-d1hRq@n+#+fDK;6%;mO!!AZtpo$v{?? zBFn&*kfNQyi%kjoDpM3Sm}^aurNJo_vYr%E7_#mZSsYYdV5r8o1{Fy&c4g>g4ruix zsIvfHkBVsmd{ru{6ukEXTXu<3DuB#IQ-Ri-fvQ0CdhnTpt_q7;&~`q|6wn$^R0pFd zfVbrdsKXXsupvIARjjBsp(udcgj#KZypN&|eLxkY0Md5H?ImP+xILIYLRN<1A@r55 zNL~TYFrlq?MV3Zh^@Z)|Ei0Kv@TiWC!|cScDXGr7LvU z7qli8Lk_+&7K=<#NoEP?(0enK_s&Uo|G}e0oy8Hgl6=$ zzV#`_DHWFSCdTo8p*}wGAs(TA-tjJuA&v|%3B8n@9ME~yhDPQlCYFYV7Ul{lzyQiK zv9y4)61aRAnHh`?zkBmBGB9v3fB~ak3h3|%m0&QBfq}t^fq|h1#(~lf3<6GinJI}S zi7G}=AqEi!28MGC3=EH;0?Z5y3=Rwi2cYWApu!A%AO<5s9f<3|pm2bRfq_AoffK}K z;6e73I)ktPI|Bm)$Uu-R7Xt&sr63{S%;ci{;{3D{1&tt01s_j8Z;&K9=7DMi8Nk56 z!oa}5&LF_Rz`zP-AzQ=1-~ih6s+XB^DQFJE|Nk%xL3%)@g51Z%0QHTL0|Nut#ThUW zu>U|#WQK{DfYg9=!qu1}L_p$-AOea(rZX^DL3tpGi6Ml6fgzr(HQv-qEk>q>NU4Ph zoLY7V_;yQURpFYgC<3V1TF?}4)tLKm0l`fKk`71|3KZ402+Qm zjyjS{OEXjKcIq-PFfcM;wG%W30vcBVAGL>KImxLApY2TeZ3mCOP-wd)cE1~f%T%0x zhmHS$m#Nau1uVGz4|4%{MvXETnBs_kWpEt=4Fim=D3sZ04l6p)(mANAWrO(|ydec? z@d)}B7ZfKTbBHOu%y8s1J#eiF^)=W9(7q$8?G`B64246C?WV@q(;cY3Ar?lv}my|&%nUI1$P7T{vj0WNcOWC4*N727#O(W z_F>&5ze^@N_PU$B{Wi*$wMxp!wT?fq_8)r`@o^8+;2WvX!L!9gqEj z1nmcJ1fa}*9ObwM0|SE)vi(R~+rayr3{bp^!XYZ$@#HUI9QJ|-#=%?aP`pfXse-TW z5<#;Y=TN(7Yk?4nSmENpJm|;iylv7#JADu)6`Vvk=8< zWDYUDH^US6;@EA5R^b>;Ws>9G6i0qjV_;yAK(iY%_5h9PJ}ePecmMPNaRCD3&5~h>BS}rHM3lJ7L>Fk*y**eDS3y89eqvR-vO?8dTtl`?zrwFl*p?8VsVin=Qn#SzFHqT(AqeuvuI)n{N}kjLi+_-0#V z8_6ic@Yt^aw;#MM1;-{{%It>^JfXE7KQCf&dIdL2q=_Q5tCQ|3c_ zb)_<@ji8A~?3O{Bzyi*+6+b97-Ox&B}7}+pq0Ns5E>*Is-H_|WzEDqq)yoP8U z6q3t$a~$Q2B?AM4Cc%(^xdXDX9VI|Wb_u>zsYTEw;0*>S4nXD*6(cxWji6BoZM^m) z`b_B2L9!cg^i@IA8#+X}0h~L~5(PC~p-ZGIz^4WvTTFTm!4n^PM7aXIn1UiV;2ZPS zM{@(x83&Zvi6adtF)%O~pxOyJqyV(Dk|G=N^_L8>*$Ao+VJ9+BWHY|rq7gouVFe)- zM)~o%!I&5~P+_beUu|GQ$PI|%$B5#QSUk0oDY5RL#<)M89-bLCcR&u(peQZk>uH%| zvlEd9jnSGTq}P2o!W}fvWr5FbP;rkwNJ&-OE%DoqIZR2q?Rdsatnk?m-e7|g<)qt< zqxA#oS7l&eu*YUU^n4l0QXankt^=yApp#|5caBkJBfc3YM@$=0&cmUd%}%&&#%$7) zoVM_VvNNX5;PZPZ^edkH>MK$ zZz6Mu>8+XK=p869Ffh2I*$O=#3E2vgdn5S9xjiuL1D)+-MDdsyo|4HE)lSGFYw+4+ zO5+n>JID*wR`78Mpk@%_lqi%yCOyC6@v%3W-N<(jQf8|ep0Rx&9JWGFwL-BNnL|t( z##bWyV%Q7nbRiDQLbi*H(N;X+?1y19;t(#{+3JsGD{O~5<)fYWa&7>ct%yUzDDx@4 z9(5q9otTG_p;(Q~Au3JbX-x-Vw;8%6h6*8#XXGLn)o#SuX~-TVy;R22PYgk`F*`F4 zb|f2RcH*evL9=3^*zE+>2IxbBB$ti&#wo&Z*o%3B9E#UTc7rLN7!F5s19;yMX!ih$ zmB<`o(lfpmTm-s}MVTd_i|>%_Afs%=xG3_s#1 zB|vNZqA_ixLQflCDI9}oBlON%%6y4$^;IlZJ3(z3$l_s2=eY2#;f%v;H>g{0O5q4T zKHK97*iMC!dpv7^67bp%n@<9rx<^^uo8w5~pr!nYM7aU9f`a1F6&z&=XzgJVURPjt zev!k2m{JAbd|EP2yAgw1=oJ>J_T!u7O(AGMC>NNa1wS=ikV=FLz{f=*TS{{K1YaDa z5p)6N^H*j#+WDaI&2+5xqn`(g;(e0a3;23g8CdOy9Qo#e)`>-S1uMMd@<>2*22e3p&JX6n^-_||LX5a9x<_DT%z1S zd2YwImLiV`H&C(m!_h~vW?*2*C&4A4I*`&)EPUy+fC#rxUK5(*sD(kRDhdg>fQt1Y zj=rh|0|P@5k*)ye6toCIEJB`yQbMFVD6b(+ag6-K|+#bL;R$hj%EjcH#D6u5J2wX&mxlvpj;wTM3BNpXwTd^I&jN)06Ykz#} z8!NEb3$6Rn8=j=wi(`EwXeE3l%wBAVMx%HanL|vz!Z+_y1-CUbClz`q30n3c-IqAp zcA$C7Y9#y6&$veR7wN4Hd?~mFhppgKtWm5*<`5OOIC^uS@rqhxTVcB?(7W%Xhc1rN z3A75U4vURc@FSjFT90faYWtC5pWB zA?(C*kUE9-;wh1u;r3!VpdH0pWDZfe29K>RD7K=V`;Kf8>2(~Qa;X(=E9k&@$AW^? zycFk*%$yYTDvsp*im#Q~hGHw8W9ljMH=Z)89mNHR)9+C%B-zjS+AbX^HbPI;N3jl> zLriMIH>cH!VjGsj`jIUoIYr?c#ppt@8P^>EDAto6&N%v(ptT0w2>X%m7C^BOnL|uS z<7+qd!0dxvNdP&l9(2tB#d!$d7=16gz2Lcj%EwLc%_#LD>;&JV0KZ}ZIY3EHP54?c z{b)AA_N9VvZlKIoJhj~fWLr@#gg~*9^mL1-B%X+5C;CMbl-Y`}oS1}UE7l7vP^?Dg z5R-54)d7?7*^C%?L0ia6stfS>eF{DofR3_6wv6OdjjxQHipyrC!3{KzlbqJ@**}et z{g^{Mq}z|9j0Wxbo{nUHKxRd14y2ybG``5Z@?23oZ~3&VEIyH}9yqpsg)W7rOY)8H-2H7UkeT}caoQr8M=rD8&(>K2L(ep5Dgq;b5W-00U8AodxbV|*9 zBwL|1G}?_h6oxU5`D@VL=LImk;iu7q?z*ASK0NjGLJa#5H~dg&E1nUKMHse1Zw^AS z5}89(p20El3_7=AG0aBL9Vno6te~+^$c046){tH=;z>tKu-XZ43{bwi1CQNHvDyu* zkg1TS@Pzd;taf8A@<9%GqT&}%I$n;|PSC|f6t_I^)IuxJZAHEFi3(@r;hXzhiQjfa zLPHAzQo|kJIfbk6yMYRO_3*WSRwLUEKM5X`wIQpl(5CsRT1%|KYd1!Nn{>Nzq;}BS zinVBVBi3$H(eHTdUq{e>D)=8yi+eqa{jjy%AiqOzD??7=q?hS9>Sxf|c^i;z#&QW7 zip8YIIUYZ6M6nrhLm4Ecqi+#Kb_X$Ks~L`wYtT-?O?YfZ6f$T_3aRS%&3IfudA)+K zgxP|_X4w5|XiLaQ_ce}s6m%BnRvdQ2YH{$zZO9Q#dP>J*`!)i$V{VN^b_!8x4o3?S zw0dzn4%=ay2`EnQc=GrTWV<1=VW6S~dqyKY#&OJ=fKJERiQ8tJ8I9yn$K&%|xb4Tx zW~BQZPwBQByUnm{hSuF9+0S^|aC@-Zj60K&?r%J{@5OIBW+o%uc08fJ54-JVl6U~I?9O3STVYqcQM|SQ-}yO5Q0;_V-U+$c6WMDdr)qq59>r!S zKfl!S|tnZ+4_SW^t66pw$~EP;Cd_dJ4H@6ve|N z=No+cc}`-n6Mh#d$a1ui6yB;d7vBd&?1=RG8|v+d=7) zL5#2Q&3E6$Vl(1?ab%mwnD53h0tT8hyobeJaO)PWkV19=QGUgfj_2e z244z~Vkya`1-`ZJk5O!ePw#`z$U@(eg6s-n!Wmx+{t1e`keeAm%QnywG0A?#R|7mn zu@hcQQQjxT(|Ue}-A>3IqLiPNg(v?!hud6~T2PdqoLXEAS&T*T=_L48y}iI?D<~^L z#;T1dZMEXF|0N;&!IwZIr)1JgEqrIvyuxQcV)T~cn8)Xa*TlGi3g`FYo1uCGa|5Ck zjdmY2WpRM7=kgZQZr6$eO53*h#xLJt+6Ws>L@UsceN9vuieohmXb;$XOj~{O(7j1= z35u___<(66^+xva^b9{@+KaQwAT@;Xjk|opv^R)ez0%LP?S{-wP#n|vR`GnnZF?kD zdu%w?bAa}9e#LY{a87Db0p;i9;A@9{!(wY;X<`aSwMlx;!ZYXf9fz&KsU@Wa6!&BB zmDE2lZ4Su?jk!4HrRSt#G>%C2HNIBhPng|7smUb)pcOO0phh6Yq$2mVQ)h9DOqXgrk1kz&=kF9@E zZFR{kN=+^S4Vh6Khxp3ne>iMS%z=8D(zS&+YF*Ge?Ek2Cd!*)O7K2aygIJAzNe{`P zjjwmgzzDq~C)fiN$KVSTGn3)IMqBJeYE6jm{2)ez&9EBZhzjW$M>vC8H%yo|8q?NB zW=tDRXlo-2rj4ewwUHImMl;H7#Fu8-5HvMcP0uCg*i}JHllS@DsT7mDLMC<;NoJ(=ElR;;jt`pY6hgBZR2oB0SEri4@z^d*z;-J59Y@&+ znq3w_wjH`)7v^j9AxV?0hYjNFFd@_t*2^uOX9Sj3g@=rs2xFPnn@ws3t1}*3vSBO7moQz(0%}E zT=v3)8{K+Ra~Hl@SQ%WlVs!8OS#37?OZFl`Pg$^;LFVOUJ6kMX5oWw^ai+prG3k!&N5`X96! zMg?YLXqaP3PGT|iNI-M6VGEK&7vH#rDw>@EiOJaxCTJTsNVgHsysH|9jYTDy;2|+H zv_>V#VT&i9s>5szOU%hk0T)ITkHF!ZOV>cL51d*dmZC)_$$rGqk^+rzYQpVIO*W$L zY$A^O4Yb}y3)xoZ#FE_7oSdNiT#8F0e5qC&*-nqtqFm4w9~93S<0}nxkZq*e+#0^I ze_do7aWBRoId9@CYxS_&iG48!sVNtqz4}<~bBiUXYB`&DHXMo#Y?4Bk) zv~iS4pnk3)R@)(o3&Y!_+l(X6g4(1;cx=Y*aZ*Da-wdKLlI_NHE2m75ZKYE=Wr}Pk zUCJplWE&w#2{dC!Y0Zjf9>N^i#sJ7zE=J)`a_Ys`(zn23BP7+LAI?H@s>M-OfLdpk z$hOj{JhsAWC&aJl)g7sR#aA9%W3`v|<*^NJd$D_(WpjIBnm%<#d+6nP1#km+y8`%-bUK6_2Jx<8BQnT*C(YgSQOgN+1 zS(IN)g_Y9y+TmXgQyt)r)lNuGp|lRb*W>Yk*&Cjk MlY-b=fYy=#0AShYpa1{> literal 0 HcmV?d00001 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