Adding miniupnpd

This commit is contained in:
Thomas Bernard 2011-09-28 21:13:20 +02:00
parent c183a72c46
commit 0d96346588
94 changed files with 18739 additions and 0 deletions

717
miniupnpd/Changelog.txt Normal file
View File

@ -0,0 +1,717 @@
$Id: Changelog.txt,v 1.240 2011/07/30 13:14:34 nanard Exp $
2011/07/30:
netfilter : Added a tiny_nf_nat.h file to compile with iptables
installed headers.
include xtables.h instead of iptables.h
VERSION 1.6 : released the 2011/07/25
2011/07/25:
Update doc for version 1.6
2011/07/15:
Fixing code with MULTIPLE_EXTERNAL_IP defined.
2011/06/27:
IPv6 support for UPnP events.
Security checks in UPnP events.
2011/06/22:
Remote host for GetListOfPortMappings
Remote host support for ipfw (tested on Mac OS X)
2011/06/20:
support for iptables-1.4.11.1
2011/06/18:
Remote host support for pf version
2011/06/04:
Supporting RemoteHost (mandatory in IGD v2)
2011/06/03:
Enabling events by default
2011/06/01:
Fixing Timeout missing in SUBSCRIBE renewal responses
(thanks to Pranesh Kulkarni)
Added comments about changes between IGD v1 and IGD v2
2011/05/28:
Description and leaseduration kept in ipfw version of the code.
Fixing ipfw code after testing under Mac OS X 10.6.7 (darwin 10.7.0)
2011/05/27:
Finishing and testing LeaseDuration support under OpenBSD.
Changing NAT-PMP port mapping lifetime support to match
lease duration support.
NAT-PMP address change announce broadcasted to both port
5350 and 5351 to be compatible with client following the
version of NAT PMP specification from 2008 or earlier.
writepidfile() Overwrite file if already existing
2011/05/26:
fix in linux/getifstats.c.
See http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2212
Implementation of LeaseDuration support.
2011/05/23:
added get_wan_connection_status_str()
2011/05/20:
adding ifacewatcher thanks to Alexey Osipov
GET /DP.xml is now available. The description has to be completed.
2011/05/19:
Add getconnstatus.c/.h. Dont always have ConnectionStatus to "Connected"
Events for WANIPv6FirewallControll
2011/05/16:
patches for gentoo linux.
generation of the DeviceProtection service description.
2011/05/15:
Making the SSDP receiving socket work in IPv6 !
2011/05/14:
Support for HTTP in both IPv6 and IPv4.
IPv6 for SSDP receiving socket.
2011/05/13:
add new options in genconfig.sh (IGD_V2, ENABLE_DP_SERVICE)
add global vars ipv6fc_firewall_enabled and ipv6fc_inbound_pinhole_allowed
have MACROS for magical values in upnpdescgen.c, add eventing vars for WanIPv6FirewallControl.
applied 0001-Cosmetic-changes.patch(see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=764)
applied 0002-Remove-lan-addresses-limit-by-changing-storage-type-.patch
replaced some of the urn:schemas-upnp-org:device:* literal strings by macros.
adding some support for IP v6. #define ENABLE_IPV6
added -fno-strict-aliasing to compile options.
2011/05/09:
updating upnp descriptions for IGDv2
2011/05/07:
Adding WANIPv6FirewallContro to upnp description
2011/04/30:
adding a UPNP_STRICT config macro. Use it now for checking RemoteHost.
ENABLE_6FC_SERVICE : add the implementations of WANIPv6FirewallControl actions
2011/04/11:
preparing getifaddr() for IP v6
preparing SSDP stuff for IP v6. Trying to conform to UDA v1.1
2011/03/09:
Some modifications thanks to Daniel Dickinson to improve OpenWRT
build.
Fixed some warnings.
2011/03/03:
Added code to generate devices/services descriptions for IGD v2
(to be continued)
2011/03/02:
improved netfilter/delete_redirect_and_filter_rules() in order
to remove the right filter rule, even if it has another index than
the nat rule.
2011/03/01:
clean up an fixes to make netfilter/testiptcrdr compile
2011/02/21:
Make "Makefile" work under Mac OS X with bsdmake.
added get_portmappings_in_range() in ipfwrdr.c
2011/02/07:
added get_portmappings_in_range() / upnp_get_portmappings_in_range()
2011/02/06:
Implementation of GetListOfPortMappings
2011/01/27:
Reverting "fixes" done in linux/iptables code the 2010/09/27.
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=741
2011/01/04:
added MINIUPNPD_VERSION in config.h. Taken from VERSION file.
VERSION 1.5 : released the 2011/01/01
2011/01/01:
Started to implement some of the new methods from WANIPConnection v2
2010/09/27:
Some fixes in the linux/iptables code when
miniupnpd_nat_chain <> miniupnpd_forward_chain
2010/09/21:
Patch to support nfqueue thanks to Colin McFarlane
2010/08/07:
Update Mac OS X / ipfw stuff from Jardel Weyrich
Fix in Makefile.linux for x86_64
2010/05/06:
Bugfix un CleanNATPMPRules() : see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=640
2010/03/14:
Fixing natpmp sockets.
2010/03/08:
Fix Makefile.linux to compile properly under Mandriva/rh/Fedora with
Iptables >= 1.4.3
Workaround for bad uptime when started with a bad time set.
2010/03/07:
Tried to make a OpenBSD version 4.7 compatible code... still some
issues.
2010/03/06:
updates to testobsdrdr
2010/03/03:
-lip4tc in Makefile.linux.
2010/02/15:
some more error handling in set_startup_time()
silencing some warnings
2010/01/14:
Open Several sockets for NAT-PMP to make sure the source address
of NAT-PMP replies is right.
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=609
2009/12/31:
miniupnpdctl now output command line arguments.
added a -h option to get help. improved help.
2009/12/22:
using PRIu64 format to printf u_int64_t
Fixing calls to get_redirect_rule_by_index() : ifname should be initialized.
Add header lines to miniupnpdctl output
2009/11/06:
implementing sending of ip address change notification when receiving
the signal SIGUSR1
VERSION 1.4 : released the 2009/10/30
2009/10/10:
Integrate IPfilter patch from Roy Marples.
Fix Netfilter code for old netfilter :
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=584
trim the description string in reload_from_lease_file()
2009/09/21:
Fixing unclosed raw sockets bug with netfilter code.
2009/09/04:
Fixes in ipf code thanks to Roy Marples
Enable DragonFly BSD Support thanks to Roy Marples.
Allow packager to define default location of config file via CFLAGS
Respect $DESTDIR when installing
2009/08/20:
Adding some support for MacOS X and IPFW
SO_REUSEADDR in minissdp.c for SSDP listening socket
2009/06/05:
unlink lease file in reload_from_lease_file()
2009/05/16:
Fixed a buffer overflow in ProcessSSDPRequest()
2009/05/11:
improving genconfig.sh for NetBSD : detecting use of pf or ipf
VERSION 1.3 :
2009/04/17:
working support for iptables >= 1.4.3
2009/04/13:
work to support iptables-1.4.3 and up
2009/04/10:
fix in upnpevents_removeSubscriber()
2009/02/14:
added reload_from_lease_file()
2009/02/13:
Changes in upnpdescgen.c to allow to remove empty elements
strcasecmp instead of strcmp on path comparaisons to allow
bugged clients to work
2009/01/29:
Some minor changes to Makefile
improving Makefile.linux in order to build with iptables not properly
installed on the system.
2009/01/23:
Fixing upnpevents thanks to Justin Maggard
2008/10/15:
getifstats() return -1 when supplied with bad arguments
2008/10/11:
Fixed NAT-PMP response when IP not allocated to external interface
2008/10/09:
adding testgetifaddr
Reporting Unconnected status when the "external interface" has
no IP address assigned. Also added some comments
VERSION 1.2 :
2008/10/07:
updating docs
2008/10/06:
MiniUPnPd is now able to use MiniSSDPd to manage SSDP M-SEARCH answering
2008/10/03:
You can now let miniupnpd choose itself the HTTP port used.
2008/10/01:
Improvements in genconfig.sh for detecting ipf or pf (under FreeBSD)
and improve debian/ubuntu stuff.
custom chain name patch from :
http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=493
2008/08/24:
added USE_IFNAME_IN_RULES macro that can be disabled in order to
remove interface name from rules.
2008/07/10:
Fixed compilation without ENABLE_L3F_SERVICE
2008/04/27:
correct UNSUBSCRIBE processing
2008/04/25(bis):
changed iptables_removeall.sh and iptables_init.sh in order
to remove IP from the rules
VERSION 1.1 :
2008/04/25:
Eventing is allmost completly implemented
2008/04/24:
Correct event handling ?
2008/04/08:
enabling tag in PF rules. quick can be set off.
2008/03/13:
implementing event notify
2008/03/11:
fixing a command line parsing error
2008/03/09:
optimisations in upnpsoap.c
2008/03/08:
optimizing upnpsoap.c for size
2008/03/06:
Worked on the Eventing : generating XML event notifications
Send initial notification after subscribe
Improved pretty print of testupnpdescgen
Reduced Memory usage of upnpdescgen
fixed a small bug in the description
2008/03/03:
Fixed miniupnpd.c for compiling without natpmp support
fixed presentationURL not there with L3F
fixing lease file creation/modification
2008/02/25:
Rewrite of Send501() and Send404()
More work on events
genconfig.sh autodetects pf/ipf
2008/02/24:
Started to implement UPnP Events. do NOT use it at the moment !
2008/02/21:
Added support for the Layer3Forwarding Service
added init_redirect() and shutdown_redirect() functions
2008/02/20:
Removed Ext: HTTP header when useless
enabled the dummy service by default to please windows XP !
2008/02/07:
upnp_enable patch by Nikos Mavrogiannopoulos.
lease_file patch by Nikos Mavrogiannopoulos.
2008/01/29:
some changes to Makefile.openwrt
use daemon() - daemonize() is still available for systems lacking daemon()
VERSION 1.0 :
2008/01/27:
moved lan_addr to upnpglobalvars.h/.c
Adding experimental multiple external IP support.
2008/01/22:
removed dummy service from description to improve compatibility
with emule client
Add "secure mode". put runtime flags in the same variable
2008/01/14:
Fixed a bug in options.c for the parsing of empty lines.
2008/01/03:
Fixed CleanExpiredNATPMP()
2008/01/02:
Adding a queue parameter for setting ALTQ in pf
2007/12/27:
improving some stuff with the PF_ENABLE_FILTER_RULE.
2007/12/22:
Adding a runtime option to enable/disable NAT-PMP
2007/12/20:
Added a cache in linux getifstats(). Please enable by editing config.h
2007/12/14:
Updating an existing NAT-PMP mapping now works
2007/12/13:
NAT-PMP code now remove expired mappings
TCP/UDP where swapped in NAT-PMP code
2007/12/04:
Adding details to the error message for sendto(udp_notify)
2007/11/27:
pf code doesn't generate filter rules by default anymore. The
#ifdef PF_ENABLE_FILTER_RULES must be uncommented in config.h.
2007/11/02:
moved some of the prototypes common to all firewalls to commonrdr.h
Added functionalities to NAT-PMP
2007/11/01:
Debugged NAT-PMP code
2007/10/28:
Cleaning and improving NAT-PMP code
2007/10/25:
improved the NAT-PMP experimental support
updated README and INSTALL files
2007/10/24:
Adding support for NAT-PMP (from apple !)
2007/10/11:
Checking the commandline for errors.
2007/10/08:
Improved the BSD/Solaris Makefile
Merging last code from Darren Reed. Solaris/IPF should work now !
added a man page.
2007/10/07:
Adding Darren Reed code for ipf.
2007/10/06:
Adding SunOS support thanks to Darren Reed.
Reorganizing os/firewall dependent code thanks to Darren Reed.
2007/09/27:
linux make install support PREFIX variable
2007/09/25:
reorganizing LAN sockets/address to improve multi LAN support.
SSDP announces are sent to all configured networks.
SSDP responses are "customized" by subnetwork.
2007/09/24:
prototype code to remove unused rules
miniupnpdctl now display current rules
synchronised add_filter_rule2() prototype between pf and netfilter code.
2007/09/19:
Correctly filling the Cache-control header in SSDP packets
2007/08/28:
update PFRULE_INOUT_COUNTS detection for FreeBSD
2007/08/27:
update version in genconfig.sh
do not error when a duplicate redirection is requested.
2007/07/16:
really fixed the compilation bug with linux>=2.6.22
2007/07/04:
fixed an error in options.c that prevented to use packet_log option
2007/07/03:
improved genconfig.sh
fixed a compilation bug with linux>=2.6.22
2007/06/22:
added PFRULE_INOUT_COUNTS macro to enable separate in/out packet and
bytes counts in pf for OpenBSD >= 3.8
2007/06/15:
removed a possible racecondition in writepidfile()
2007/06/12:
improved genconfig.sh : no more "echo -e", use lsb_release when available
2007/06/11:
get_redirect_rule*() functions now return some statistics about
rule usage (bytes and packets)
2007/06/07:
Fixed the get_redirect_desc() in the linux/netfilter code
2007/06/05:
Clean up init code in miniupnpd.c
Added a syslog message in SoapError()
2007/06/04:
Now store redirection descriptions in the linux/netfilter code
2007/05/21:
Answers to SSDP M-SEARCH requests with ST: ssdp:all
added make install to Makefile.linux
2007/05/10:
Fixed a bug int the DeletePortMapping linux/netfilter implementation
It was allways the 1st rule that was deleted.
2007/04/26:
Fixed config.h.openwrt
2007/04/16:
added something in the INSTALL file about the FreeBSD send(udp_notify)
problem fix (allowing 239.0.0.0/8 explicitely in pf.conf)
2007/03/30:
added setsockopt(s, SOL_SOCKET, SO_BROADCAST ...) for broadcasting
socket
2007/03/17:
Fixed filter rule under linux : it was using wrong port !
thanks to Wesley W. Terpstra
2007/03/01:
Moved some of the SSDP code from miniupnpd.c to minissdp.c
2007/02/28:
creating miniupnpdctl
2007/02/26:
use LOG_MINIUPNPD macro for openlog()
simplify miniupndShutdown()
2007/02/09:
improved genconfig.h
Added stuff to change the pf rule "rdr" to "rdr pass"
2007/02/07:
Corrected Bytes per seconds to bits per second.
Ryan cleaned up comments and typos.
Ryan cleaned up daemonize stuff.
Ryan added possibility to configure model number and serial number
2007/01/30:
ryan improved the robustness of most UPnP Soap methods
I added a target in the Makefiles to properly generate an uuid using
command line tools.
Improved configuration file parsing.
2007/01/29:
Adding uuid option in miniupnpd.conf
2007/01/27:
Added upnppermissions stuff : adding some security to UPnP !
fixed XML description thanks to Ryan Wagoner
improved QueryStateVariable thanks to Ryan Wagoner
2007/01/22:
use getifaddr() for each GetExtenalIPAddress() Call.
We can change the ip during execution without pb
2007/01/17:
Lots of code cleanup
2007/01/12:
Fixed a nasty bug in the linux/netfilter version of get_filter_rule()
2007/01/11:
Improved the handling of the miniupnpd.conf file.
added -f option to choose which config file to read.
2007/01/10:
Fixed potential bugs with ClearNameValueList()
2007/01/08:
All by Ryan Wagoner :
- coding style and comments cleanup
- using now option file miniupnpd.conf
2007/01/03:
changed "xx active incoming HTTP connections" msg
2007/01/02:
Patch from Ryan Wagoner :
- no need to open sockets if we can't set the error handlers
- format the usage so it fits nicely on a standard size terminal
- fix up log_err message so they have the same format and you know what
they are related to
- use same "white space" style throughout
- on shutdown no need to continue if opening socket or setsockopt fails
2006/12/14:
reduce amount of log lines (keeping the same information)
2006/12/07:
Fixed Makefiles
fixed typos in logs
version 1.0-RC1 released
2006/12/02:
moved strings from upnpdescgen.c to upnpdescstrings.h for
easier modification
Server: HTTP header now comes from a #define
added a compilation-time generated config.h
2006/11/30:
minixml updated. should have no impact
Added support for presentationURL with -w switch
implemented getifstats() for linux. Added testgetifstats program
improved error handling in getifstats() BSD
2006/11/26:
no need to have miniupnpc sources to compile miniupnpd.
Makefile.openwrt updated
Closing sockets on exit thanks to Ryan Wagoner
2006/11/23:
now handling signal SIGINT
setting HTTP socket with REUSEADDR thanks to Ryan Wagoner
daemon now tested on a Linksys WRT54G device running OpenWRT !
2006/11/21:
disabling rtableid in pf code.
2006/11/22:
Also responds on M-SEARCH with the uuid
2006/11/20:
gaining some space in upnpsoap.c
2006/11/19:
Cleaning up code to comply with ANSI C89
2006/11/17:
Linux version now deleting both nat and accept rules
implemented -U option under Linux
2006/11/16:
implemented delete_redirect_rule() for linux
returning error 714 in DeletePortMapping() when needed
2006/11/12:
The linux/netfilter version should now WORK !
fix in the writepidfile() function. open with a mode !
2006/11/10:
fixing the XML description generation for big endian machines
working on the linux/netfilter port
2006/11/09:
improved a lot the handling of HTTP error cases
2006/11/08:
Tried to make the Makefile compatible with both BSDmake
and GNUmake. It was hard because of $^ and $<
2006/11/07:
Makefile compatible with BSD make
make install target.
getifstats.c compatible with both OpenBSD and FreeBSD.
2006/11/06:
added getifstats.c for openBSD. May not work under FreeBSD ?
now reports bytes/packets sent/received
reporting bitrates
possibility to report system uptime
2006/10/29:
added a -L option to enable loggin (is off by default now).
2006/10/28:
Patch by Ryan Wagoner to correct the XML description (was NewUpTime
instead of NewUptime) and implement uptime.
Trying to fix the memory leak. Added some comments
added a -d option for debugging purpose
Tnaks to valgrind (under linux!) I removed a small memory access error.
2006/10/27:
Thanks to a patch sent by Michael van Tellingen, miniupnpd is
now ignoring NOTIFY packets sent by other devices and is
writing is own pid to /var/run/miniupnpd.pid
2006/10/23:
Allways set sendEvents="no" in XML description (was causing
pb with winXP as SUBSCRIBE is not implemented)
2006/10/22:
added translation from hostname to IP in the AddPortMapping() method
Thanks to Ryan Wagoner.
2006/10/18:
Added an INSTALL file
2006/10/13:
Added the possibility to change the notify interval
2006/09/29:
Improved compliance of the XML Descriptions
pretty print for testupnpdescgen
2006/09/25:
improved the Error 404 response.
Better serviceType and serviceId for dummy service...
2006/09/24:
updating the XML description generator
2006/09/18:
Thanks to Rick Richard, support for SSDP "alive" and "byebye" notifications
was added. The -u options was also added. The SSDP response are now
improved.
The -o option is now working (to force a specific external IP address).
The Soap Methods errors are correctly responded (401 Invalid Action)
2006/09/09:
Added code to handle filter rules. Thanks to Seth Mos (pfsense.com)
storing the descriptions in the label of the rule
2006/09/02:
improved the generation of the XML descriptions.
I still need to add allowed values to variables.
2006/07/29:
filtering SSDP requests and responding with same ST: field
2006/07/25:
Added a dummy description for the WANDevice
2006/07/20:
Command line arguments processing
Added possibility to listen internally on several interfaces

143
miniupnpd/INSTALL Normal file
View File

@ -0,0 +1,143 @@
MiniUPnP project.
(c) 2006-2011 Thomas Bernard
Homepage : http://miniupnp.free.fr/
Mirror: http://miniupnp.tuxfamily.org/
miniupnpd is still under active developpement. This documentation is
likely to be a little outdated when you read it. So please go on the
web forum http://miniupnp.tuxfamily.org/ if you need more information.
================================ *BSD/pf =================================
To Build and Install :
- use BSD make to compile.
- you can first 'make config.h' then edit config.h to your preferences and
finally 'make'
- add "rdr-anchor miniupnpd" and "anchor miniupnpd" lines to /etc/pf.conf
- some FreeBSD users reported that it is also necessary for them
to explicitly allow udp traffic on 239.0.0.0/8 by adding the two following
lines to /etc/pf.conf :
pass out on $int_if from any to 239.0.0.0/8 keep state
pass in on $int_if from any to 239.0.0.0/8 keep state
- dont forget to " pfctl -f /etc/pf.conf "
- you can check your modifications are taken into accout with
"pfctl -s nat" and "pfctl -s rule". Look for the "rdr-anchor miniupnpd"
and "anchor miniupnpd" lines.
- install as root using :
# make install
or
# PREFIX=/usr/local make install
- run as root : The daemon needs rights to modify pf rules.
edit the /etc/miniupnpd.conf file to set options. All options are also
available through command line switches.
To stop the daemon use :
> kill `cat /var/run/miniupnpd.pid`
=========================== *BSD,*Solaris/ipf =============================
genconfig.sh and the Makefile try to detect wether ipf or pf should be
used. If it fails, edit config.h and Makefile by hand.
In Makefile, the FWNAME variable value should be pf or ipf.
Installation steps are allmost the same as with pf.
*Solaris users would be interested in reading informations from :
http://blogs.sun.com/avalon/category/IPFilter
============================= Mac OS X/ipfw ===============================
- use 'bsdmake' or 'make -f Makefile.macosx' to build
============================ Linux/netfilter ==============================
To Build and install :
- make sure you have libiptc available on your system :
if you are using debian, "apt-get install iptables-dev"
Some versions of the iptables-dev package don't include the
necessary files : read "how to get libiptc with its headers on debian" below.
In anycase, libiptc is available in iptables sources packages
from http://netfilter.org
- edit and run netfilter/iptables_init.sh shell script.
This script must allways be run before the daemon
to set up intial rules and chains.
- Build and edit the config.h file
> make -f Makefile.linux config.h
> vi config.h
- Build the daemon
> make -f Makefile.linux
If not using iptables from your system,
> IPTABLESPATH=/path/to/iptables-1.4.1 make -f Makefile.linux
note : make sure you have iptables with static libraries compiled.
use "./configure --enable-static" before compiling iptables
- install as root using :
> make -f Makefile.linux install
- A miniupnpd script should be installed to /etc/init.d
and the configuration files to /etc/miniupnpd
- anytime, you can use the netfilter/iptables_flush.sh
script to flush all rules added by the daemon.
- after killing the daemon, you can get back to
iptables initial state by runing the netfilter/iptables_removeall.sh
script. Don't forget to edit the script to your convinience.
NOTE: a /etc/init.d/miniupnpd script will be installed.
If it suits you, you can use is with start, stop or restart argument.
# /etc/init.d/miniupnpd restart
How to get libiptc with its headers on debian :
- Use apt-get to get sources :
> apt-get source iptables
you should then have an iptables-x.x.x/ directory.
- configure and compile :
> cd iptables-x.x.x/
> ./configure --enable-static
> make
- it is now possible to compile miniupnpd using the following command :
> IPTABLESPATH=§path/to/iptables-x.x.x make -f Makefile.linux
=========================== Configuration =============================
Edit the /etc/miniupnpd.conf file to set options. All options are also
available through command line switches.
Miniupnpd supports some kind of security check for allowing or disallowing
redirection to be made. The UPnP permission rules are read from the
miniupnpd.conf configuration file.
When a new redirection is asked, permission rules are evaluated in top-down
order and the first permission rule matched gives the answer : redirection
allowed or denied. If no rule is matching, the redirection is allowed, so
it is a good practice to have a "catch all" deny permission rule at the end
of your mermission ruleset.
Sample permission ruleset :
allow 4662-4672 192.168.1.34/32 4662-4672
deny 0-65535 192.168.1.34/32 0-65535
allow 1024-65535 192.168.1.0/24 1024-65535
deny 0-65535 0.0.0.0/0 0-65535
With this ruleset, redirections are allowed only for host on the subnet
192.168.1.0/255.255.255.0 for the ports 1024 or above. There is an exception
for the host 192.168.1.34 for which only redirections from/to port 4662 to
4672 are allowed.
You can generate the uuid for your UPnP device with the uuidgen available
under linux. The following following OpenBSD package is also providing
a "uuid" tool :
http://www.openbsd.org/4.0_packages/i386/uuid-1.5.0.tgz-long.html
An web based uuid generator is also available :
http://kruithof.xs4all.nl/uuid/uuidgen
On linux systems, one could also use the command
'cat /proc/sys/kernel/random/uuid' to generate an uuid.
More simple, use the genuuid makefile target :
> make genuuid
or
> make -f Makefile.linux genuuid
This target is needed by the "install" target, so it should be done
automatically.
To stop the daemon use :
# kill `cat /var/run/miniupnpd.pid`
or if your linux system use /etc/init.d/
# /etc/init.d/miniupnpd stop

27
miniupnpd/LICENSE Normal file
View File

@ -0,0 +1,27 @@
MiniUPnPd
Copyright (c) 2006-2011, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* 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.
* The name of the author may not 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 OWNER 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.

195
miniupnpd/Makefile Normal file
View File

@ -0,0 +1,195 @@
# $Id: Makefile,v 1.59 2011/05/26 22:47:32 nanard Exp $
# MiniUPnP project
# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
# Author: Thomas Bernard
#
# Makefile for miniupnpd (MiniUPnP daemon)
#
# This Makefile should work for *BSD and SunOS/Solaris.
# On Mac OS X, use "bsdmake" to build.
# This Makefile is NOT compatible with GNU Make.
# Linux users, please use Makefile.linux :
# make -f Makefile.linux
CFLAGS = -pipe -Wall -Os
#CFLAGS = -pipe -Wall -O -g -DDEBUG
CC ?= gcc
RM = rm -f
MV = mv
INSTALL = install
STRIP = strip
# OSNAME and FWNAME are used for building OS or FW dependent code.
OSNAME != uname -s
ARCH != uname -m
.ifndef FWNAME
.if exists(/usr/include/net/pfvar.h)
FWNAME = pf
.else
FWNAME = ipf
.endif
.endif
# better way to find if we are using ipf or pf
.if $(OSNAME) == "FreeBSD"
FWNAME != . /etc/rc.subr; . /etc/rc.conf; \
if checkyesno ipfilter_enable; then \
echo "ipf"; else echo "pf"; fi
.endif
.if $(OSNAME) == "NetBSD"
FWNAME != . /etc/rc.subr; . /etc/rc.conf; \
if checkyesno ipfilter; then \
echo "ipf"; else echo "pf"; fi
.endif
.if $(OSNAME) == "DragonFly"
FWNAME != . /etc/rc.subr; . /etc/rc.conf; \
if chechyesno ipfilter; then \
echo "ipf"; else echo "pf"; fi
.endif
.if $(OSNAME) == "Darwin"
FWNAME = ipfw
.endif
# Solaris specific CFLAGS
.if $(OSNAME) == "SunOS"
CFLAGS += -DSOLARIS2=`uname -r | cut -d. -f2`
.if $(ARCH) == "amd64"
CFLAGS += -m64 -mcmodel=kernel -mno-red-zone -ffreestanding
.elif $(ARCH) == "sparc64"
CFLAGS += -m64 -mcmodel=medlow
.endif
.endif
STDOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
options.o upnppermissions.o minissdp.o natpmp.o \
upnpevents.o upnputils.o getconnstatus.o
BSDOBJS = bsd/getifstats.o bsd/ifacewatcher.o
SUNOSOBJS = solaris/getifstats.o bsd/ifacewatcher.o
MACOBJS = mac/getifstats.o bsd/ifacewatcher.o
PFOBJS = pf/obsdrdr.o
IPFOBJS = ipf/ipfrdr.o
IPFWOBJS = ipfw/ipfwrdr.o
MISCOBJS = upnpreplyparse.o minixml.o
ALLOBJS = $(STDOBJS) $(MISCOBJS)
.if $(OSNAME) == "SunOS"
ALLOBJS += $(SUNOSOBJS)
TESTGETIFSTATSOBJS = testgetifstats.o solaris/getifstats.o
.elif $(OSNAME) == "Darwin"
ALLOBJS += $(MACOBJS)
TESTGETIFSTATSOBJS = testgetifstats.o mac/getifstats.o
.else
ALLOBJS += $(BSDOBJS)
TESTGETIFSTATSOBJS = testgetifstats.o bsd/getifstats.o
.endif
.if $(FWNAME) == "pf"
ALLOBJS += $(PFOBJS)
.elif $(FWNAME) == "ipfw"
ALLOBJS += $(IPFWOBJS)
.else
ALLOBJS += $(IPFOBJS)
.endif
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
TESTUPNPPERMISSIONSOBJS = testupnppermissions.o upnppermissions.o
TESTGETIFADDROBJS = testgetifaddr.o getifaddr.o
MINIUPNPDCTLOBJS = miniupnpdctl.o
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \
testupnppermissions miniupnpdctl \
testgetifaddr
.if $(OSNAME) == "Darwin"
LIBS =
.else
LIBS = -lkvm
.endif
.if $(OSNAME) == "SunOS"
LIBS += -lsocket -lnsl -lkstat -lresolv
.endif
# set PREFIX variable to install in the wanted place
INSTALLBINDIR = $(PREFIX)/sbin
INSTALLETCDIR = $(PREFIX)/etc
# INSTALLMANDIR = $(PREFIX)/man
INSTALLMANDIR = /usr/share/man
all: $(EXECUTABLES)
clean:
$(RM) $(STDOBJS) $(BSDOBJS) $(SUNOSOBJS) $(MACOBJS) $(EXECUTABLES) \
testupnpdescgen.o \
$(MISCOBJS) config.h testgetifstats.o testupnppermissions.o \
miniupnpdctl.o testgetifaddr.o \
$(PFOBJS) $(IPFOBJS) $(IPFWOBJS)
install: miniupnpd genuuid
$(STRIP) miniupnpd
$(INSTALL) -d $(DESTDIR)$(INSTALLBINDIR)
$(INSTALL) -m 555 miniupnpd $(DESTDIR)$(INSTALLBINDIR)
$(INSTALL) -d $(DESTDIR)$(INSTALLETCDIR)
$(INSTALL) -b miniupnpd.conf $(DESTDIR)$(INSTALLETCDIR)
# TODO : install man page correctly
# $(INSTALL) -d $(INSTALLMANDIR)
# $(INSTALL) miniupnpd.1 $(INSTALLMANDIR)/cat1/miniupnpd.0
# genuuid is using the uuid cli tool available under OpenBSD 4.0 in
# the uuid-1.5.0 package
# any other cli tool returning a uuid on stdout should work.
UUID != if which uuidgen 2>&1 > /dev/null; then \
echo `uuidgen` ; \
elif which uuid 2>&1 > /dev/null; then \
echo `uuid` ; \
else echo "00000000-0000-0000-0000-000000000000"; \
fi
genuuid:
$(MV) miniupnpd.conf miniupnpd.conf.before
sed -e "s/^uuid=[-0-9a-fA-F]*/uuid=$(UUID)/" miniupnpd.conf.before > miniupnpd.conf
$(RM) miniupnpd.conf.before
depend: config.h
mkdep $(ALLOBJS:.o=.c) testupnpdescgen.c testgetifstats.c \
testupnppermissions.c miniupnpdctl.c testgetifaddr.c
miniupnpd: config.h $(ALLOBJS)
$(CC) $(CFLAGS) -o $@ $(ALLOBJS) $(LIBS)
# BSDmake :
# $(CC) $(CFLAGS) -o $@ $> $(LIBS)
miniupnpdctl: config.h $(MINIUPNPDCTLOBJS)
$(CC) $(CFLAGS) -o $@ $(MINIUPNPDCTLOBJS)
testupnpdescgen: config.h $(TESTUPNPDESCGENOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTUPNPDESCGENOBJS)
testgetifstats: config.h $(TESTGETIFSTATSOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTGETIFSTATSOBJS) $(LIBS)
testgetifaddr: config.h $(TESTGETIFADDROBJS)
$(CC) $(CFLAGS) -o $@ $(TESTGETIFADDROBJS)
testupnppermissions: config.h $(TESTUPNPPERMISSIONSOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTUPNPPERMISSIONSOBJS)
# gmake :
# $(CC) $(CFLAGS) -o $@ $^
# BSDmake :
# $(CC) $(CFLAGS) -o $@ $>
config.h: genconfig.sh
./genconfig.sh
.SUFFIXES: .o .c
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
# $(CC) $(CFLAGS) -c -o $(.TARGET) $(.IMPSRC)

197
miniupnpd/Makefile.linux Normal file
View File

@ -0,0 +1,197 @@
# $Id: Makefile.linux,v 1.60 2011/07/30 13:14:35 nanard Exp $
# MiniUPnP project
# (c) 2006-2011 Thomas Bernard
# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
# Author : Thomas Bernard
# for use with GNU Make
#
# To install use :
# $ PREFIX=/dummyinstalldir make -f Makefile.linux install
# or :
# $ INSTALLPREFIX=/usr/local make -f Makefile.linux install
# or :
# $ make -f Makefile.linux install
#
# if your system hasn't iptables libiptc headers and binary correctly
# installed, you need to get iptables sources from http://netfilter.org/
# ./configure them and build them then miniupnpd will build using :
# $ IPTABLESPATH=/path/to/iptables-1.4.1 make -f Makefile.linux
#
#CFLAGS = -Wall -O -D_GNU_SOURCE -g -DDEBUG
CFLAGS ?= -Wall -Os -D_GNU_SOURCE -fno-strict-aliasing -Wstrict-prototypes
CC ?= gcc
RM = rm -f
INSTALL = install
STRIP ?= strip
CP = cp
INSTALLPREFIX ?= $(PREFIX)/usr
SBININSTALLDIR = $(INSTALLPREFIX)/sbin
ETCINSTALLDIR = $(PREFIX)/etc/miniupnpd
BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpreplyparse.o minixml.o \
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
options.o upnppermissions.o minissdp.o natpmp.o \
upnpevents.o upnputils.o getconnstatus.o
LNXOBJS = linux/getifstats.o linux/ifacewatcher.o
NETFILTEROBJS = netfilter/iptcrdr.o
ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS)
ifeq "$(wildcard /etc/gentoo-release )" ""
LIBS ?= -liptc
else # gentoo
# the following is better, at least on gentoo with iptables 1.4.6
# see http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1618
# and http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183
LIBS ?= -lip4tc
endif
ARCH ?= $(shell uname -m | grep -q "x86_64" && echo 64)
ifdef IPTABLESPATH
CFLAGS := $(CFLAGS) -I$(IPTABLESPATH)/include/
LDFLAGS := $(LDFLAFGS) -L$(IPTABLESPATH)/libiptc/
# get iptables version and set IPTABLES_143 macro if needed
ifeq ($(TARGET_OPENWRT),)
IPTABLESVERSION := $(shell grep "\#define VERSION" $(IPTABLESPATH)/config.h | tr -d \" |cut -d" " -f3 )
IPTABLESVERSION1 := $(shell echo $(IPTABLESVERSION) | cut -d. -f1 )
IPTABLESVERSION2 := $(shell echo $(IPTABLESVERSION) | cut -d. -f2 )
IPTABLESVERSION3 := $(shell echo $(IPTABLESVERSION) | cut -d. -f3 )
# test if iptables version >= 1.4.3
TEST := $(shell [ \( \( $(IPTABLESVERSION1) -ge 1 \) -a \( $(IPTABLESVERSION2) -ge 4 \) \) -a \( $(IPTABLESVERSION3) -ge 3 \) ] && echo 1 )
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
# the following sucks, but works
LIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o
#LIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a
else # ifeq ($(TEST), 1)
LIBS = $(IPTABLESPATH)/libiptc/libiptc.a
endif # ifeq ($(TEST), 1)
else # ($(TARGET_OPENWRT),)
# openWRT :
# check for system-wide iptables files. Test if iptables version >= 1.4.3
# the following test has to be verified :
TEST := $(shell test -f /usr/include/iptables/internal.h && grep -q "\#define IPTABLES_VERSION" /usr/include/iptables/internal.h && echo 1)
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
LIBS = -liptc
endif # ($(TEST), 1)
TEST_LIB := $(shell test -f /usr/lib$(ARCH)/libiptc.a && echo 1)
ifeq ($(TEST_LIB), 1)
LIBS = -liptc /usr/lib$(ARCH)/libiptc.a
endif # ($(TEST_LIB), 1)
endif # ($(TARGET_OPENWRT),)
else # ifdef IPTABLESPATH
# IPTABLESPATH not defined
# the following test has to be verified :
TEST := $(shell test -f /usr/include/xtables.h && grep -q "XTABLES_VERSION_CODE" /usr/include/xtables.h && echo 1)
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
LIBS = -lip4tc
endif # ($(TEST), 1)
endif # ifdef IPTABLESPATH
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \
testupnppermissions miniupnpdctl testgetifaddr
.PHONY: all clean install depend genuuid
all: $(EXECUTABLES)
clean:
$(RM) $(ALLOBJS)
$(RM) $(EXECUTABLES)
$(RM) testupnpdescgen.o testgetifstats.o
$(RM) testupnppermissions.o testgetifaddr.o
$(RM) miniupnpdctl.o
install: miniupnpd genuuid
$(STRIP) miniupnpd
$(INSTALL) -d $(SBININSTALLDIR)
$(INSTALL) miniupnpd $(SBININSTALLDIR)
$(INSTALL) -d $(ETCINSTALLDIR)
$(INSTALL) netfilter/iptables_init.sh $(ETCINSTALLDIR)
$(INSTALL) netfilter/iptables_removeall.sh $(ETCINSTALLDIR)
$(INSTALL) --mode=0644 -b miniupnpd.conf $(ETCINSTALLDIR)
$(INSTALL) -d $(PREFIX)/etc/init.d
$(INSTALL) linux/miniupnpd.init.d.script $(PREFIX)/etc/init.d/miniupnpd
# genuuid is using the uuidgen CLI tool which is part of libuuid
# from the e2fsprogs
genuuid:
ifeq ($(TARGET_OPENWRT),)
sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`(genuuid||uuidgen||uuid) 2>/dev/null`/" miniupnpd.conf
else
sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`($(STAGING_DIR_HOST)/bin/genuuid||$(STAGING_DIR_HOST)/bin/uuidgen||$(STAGING_DIR_HOST)/bin/uuid) 2>/dev/null`/" miniupnpd.conf
endif
miniupnpd: $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS) $(LIBS)
testupnpdescgen: $(TESTUPNPDESCGENOBJS)
testgetifstats: testgetifstats.o linux/getifstats.o
testupnppermissions: testupnppermissions.o upnppermissions.o
testgetifaddr: testgetifaddr.o getifaddr.o
miniupnpdctl: miniupnpdctl.o
config.h: genconfig.sh
./genconfig.sh
depend: config.h
makedepend -f$(MAKEFILE_LIST) -Y \
$(ALLOBJS:.o=.c) $(TESTUPNPDESCGENOBJS:.o=.c) \
testgetifstats.c 2>/dev/null
# DO NOT DELETE
miniupnpd.o: config.h upnpglobalvars.h upnppermissions.h miniupnpdtypes.h
miniupnpd.o: upnphttp.h upnpdescgen.h miniupnpdpath.h getifaddr.h upnpsoap.h
miniupnpd.o: options.h minissdp.h upnpredirect.h daemonize.h upnpevents.h
miniupnpd.o: natpmp.h commonrdr.h upnputils.h ifacewatcher.h
upnphttp.o: config.h upnphttp.h upnpdescgen.h miniupnpdpath.h upnpsoap.h
upnphttp.o: upnpevents.h
upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h
upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h
upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h
upnpsoap.o: config.h upnpglobalvars.h upnppermissions.h miniupnpdtypes.h
upnpsoap.o: upnphttp.h upnpsoap.h upnpreplyparse.h upnpredirect.h getifaddr.h
upnpsoap.o: getifstats.h getconnstatus.h upnpurns.h
upnpreplyparse.o: upnpreplyparse.h minixml.h
minixml.o: minixml.h
upnpredirect.o: config.h upnpredirect.h upnpglobalvars.h upnppermissions.h
upnpredirect.o: miniupnpdtypes.h upnpevents.h netfilter/iptcrdr.h commonrdr.h
getifaddr.o: config.h getifaddr.h
daemonize.o: daemonize.h config.h
upnpglobalvars.o: config.h upnpglobalvars.h upnppermissions.h
upnpglobalvars.o: miniupnpdtypes.h
options.o: options.h config.h upnppermissions.h upnpglobalvars.h
options.o: miniupnpdtypes.h
upnppermissions.o: config.h upnppermissions.h
minissdp.o: config.h upnpdescstrings.h miniupnpdpath.h upnphttp.h
minissdp.o: upnpglobalvars.h upnppermissions.h miniupnpdtypes.h minissdp.h
minissdp.o: upnputils.h codelength.h
natpmp.o: config.h natpmp.h upnpglobalvars.h upnppermissions.h
natpmp.o: miniupnpdtypes.h getifaddr.h upnpredirect.h commonrdr.h
upnpevents.o: config.h upnpevents.h miniupnpdpath.h upnpglobalvars.h
upnpevents.o: upnppermissions.h miniupnpdtypes.h upnpdescgen.h
upnputils.o: config.h upnputils.h
getconnstatus.o: getconnstatus.h getifaddr.h
linux/getifstats.o: config.h getifstats.h
linux/ifacewatcher.o: config.h ifacewatcher.h config.h minissdp.h
linux/ifacewatcher.o: miniupnpdtypes.h getifaddr.h upnpglobalvars.h
linux/ifacewatcher.o: upnppermissions.h natpmp.h
netfilter/iptcrdr.o: netfilter/iptcrdr.h commonrdr.h config.h
netfilter/iptcrdr.o: upnpglobalvars.h upnppermissions.h miniupnpdtypes.h
testupnpdescgen.o: config.h upnpdescgen.h
upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h
upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h
upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h
testgetifstats.o: getifstats.h

116
miniupnpd/Makefile.macosx Normal file
View File

@ -0,0 +1,116 @@
# MiniUPnP project
# http://miniupnp.free.fr/
# Author: Thomas Bernard
# This Makefile should work for MacOSX
#
# To install use :
# $ PREFIX=/dummyinstalldir make -f Makefile.macosx install
# or :
# $ make -f Makefile.macosx install
#
CFLAGS = -Wall -O -g3 -DDEBUG
#CFLAGS = -Wall -Os
CC = gcc
RM = rm -f
MV = mv
INSTALL = install
STRIP = strip
# OSNAME and FWNAME are used for building OS or FW dependent code.
OSNAME = $(shell uname)
ARCH = $(shell uname -p)
FWNAME = ipfw
STD_OBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
options.o upnppermissions.o minissdp.o natpmp.o \
upnpevents.o getconnstatus.o upnputils.o
MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o
IPFW_OBJS = ipfw/ipfwrdr.o
MISC_OBJS = upnpreplyparse.o minixml.o
ALL_OBJS = $(STD_OBJS) $(MISC_OBJS) $(MAC_OBJS) $(IPFW_OBJS)
TEST_UPNPDESCGEN_OBJS = testupnpdescgen.o upnpdescgen.o
TEST_GETIFSTATS_OBJS = testgetifstats.o mac/getifstats.o
TEST_UPNPPERMISSIONS_OBJS = testupnppermissions.o upnppermissions.o
TEST_GETIFADDR_OBJS = testgetifaddr.o getifaddr.o
MINIUPNPDCTL_OBJS = miniupnpdctl.o
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \
testupnppermissions miniupnpdctl \
testgetifaddr
LIBS =
# set PREFIX variable to install in the wanted place
INSTALL_BINDIR = $(PREFIX)/sbin
INSTALL_ETCDIR = $(PREFIX)/etc/miniupnpd
# INSTALL_MANDIR = $(PREFIX)/man
INSTALL_MANDIR = /usr/share/man/man1
all: $(EXECUTABLES)
clean:
$(RM) $(ALL_OBJS) $(EXECUTABLES) \
testupnpdescgen.o testgetifstats.o testupnppermissions.o \
miniupnpdctl.o testgetifaddr.o config.h
install: miniupnpd genuuid
$(STRIP) miniupnpd
$(INSTALL) -d $(INSTALL_BINDIR)
$(INSTALL) miniupnpd $(INSTALL_BINDIR)
$(INSTALL) -d $(INSTALL_ETCDIR)
$(INSTALL) ipfw/ipfw_init.sh $(INSTALL_ETCDIR)
$(INSTALL) ipfw/ipfw_removeall.sh $(INSTALL_ETCDIR)
$(INSTALL) --mode=0644 -b miniupnpd.conf $(INSTALL_ETCDIR)
$(INSTALL) -d $(INSTALL_MANDIR)
$(INSTALL) miniupnpd.1 $(INSTALL_MANDIR)
# TODO Fix these paths and those within the plist
$(INSTALL) -d $(PREFIX)/Library/LaunchDaemons
$(INSTALL) mac/org.tuxfamily.miniupnpd.plist $(PREFIX)/Library/LaunchDaemons
# genuuid is using the uuid cli tool available under MacOSX
UUID != if which uuidgen 2>&1 > /dev/null; then \
echo `uuidgen` ; \
elif which uuid 2>&1 > /dev/null; then \
echo `uuid` ; \
else echo "00000000-0000-0000-0000-000000000000"; \
fi
genuuid:
$(MV) miniupnpd.conf miniupnpd.conf.before
sed -e "s/^uuid=[-0-9a-f]*/uuid=$(UUID)/" miniupnpd.conf.before > miniupnpd.conf
$(RM) miniupnpd.conf.before
depend: config.h
mkdep $(ALL_OBJS:.o=.c) testupnpdescgen.c testgetifstats.c \
testupnppermissions.c miniupnpdctl.c testgetifaddr.c
miniupnpd: config.h $(ALL_OBJS)
$(CC) $(CFLAGS) -o $@ $(ALL_OBJS) $(LIBS)
miniupnpdctl: config.h $(MINIUPNPDCTL_OBJS)
$(CC) $(CFLAGS) -o $@ $(MINIUPNPDCTL_OBJS)
testupnpdescgen: config.h $(TEST_UPNPDESCGEN_OBJS)
$(CC) $(CFLAGS) -o $@ $(TEST_UPNPDESCGEN_OBJS)
testgetifstats: config.h $(TEST_GETIFSTATS_OBJS)
$(CC) $(CFLAGS) -o $@ $(TEST_GETIFSTATS_OBJS) $(LIBS)
testgetifaddr: config.h $(TEST_GETIFADDR_OBJS)
$(CC) $(CFLAGS) -o $@ $(TEST_GETIFADDR_OBJS)
testupnppermissions: config.h $(TEST_UPNPPERMISSIONS_OBJS)
$(CC) $(CFLAGS) -o $@ $(TEST_UPNPPERMISSIONS_OBJS)
config.h: genconfig.sh
./genconfig.sh
.SUFFIXES: .o .c
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
# $(CC) $(CFLAGS) -c -o $(.TARGET) $(.IMPSRC)

30
miniupnpd/README Normal file
View File

@ -0,0 +1,30 @@
MiniUPnP project
(c) 2006-2011 Thomas Bernard
webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
contact: miniupnp@free.fr
This directory contain the miniUPnP daemon software.
This software is subject to the conditions detailed in
the LICENCE file provided with this distribution.
The miniUPnP daemon is an UPnP IGD (internet gateway device)
which provide NAT traversal services to any UPnP enabled client on
the network.
See http://www.upnp.org/ for more details on UPnP.
During the year 2011, support for IGD v2 has been added.
This support is experimental, and should be enabled only
for testing and development.
Later, support for the NAT Port Mapping Protocol (NAT-PMP) was
added. See information about NAT-PMP here :
http://miniupnp.free.fr/nat-pmp.html
Read the INSTALL files for instructions to compile, install and
configure miniupnpd.
Report bugs to miniupnp@free.fr or on the web forum :
http://miniupnp.tuxfamily.org/forum/
Thomas Bernard

1
miniupnpd/VERSION Normal file
View File

@ -0,0 +1 @@
1.6

20
miniupnpd/bsd/Makefile Normal file
View File

@ -0,0 +1,20 @@
# $Id: Makefile,v 1.2 2011/05/20 09:34:25 nanard Exp $
# made for GNU Make
CFLAGS = -Wall -g
EXECUTABLES = testgetifstats testifacewatcher
all: $(EXECUTABLES)
clean:
rm -f *.o $(EXECUTABLES)
testobsdrdr.o: testobsdrdr.c obsdrdr.h
testgetifstats: testgetifstats.o getifstats.o
$(CC) $(CFLAGS) -o $@ $> -lkvm
testifacewatcher: testifacewatcher.o ifacewatcher.o upnputils.o
$(CC) $(CFLAGS) -o $@ $>
upnputils.o: ../upnputils.c

133
miniupnpd/bsd/getifstats.c Normal file
View File

@ -0,0 +1,133 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner and Thomas Bernard
* (c) 2006 Ryan Wagoner
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#if defined(__FreeBSD__) || defined(__DragonFly__)
#include <net/if_var.h>
#endif
#if defined(__DragonFly__)
#include <net/pf/pfvar.h>
#else
#include <net/pfvar.h>
#endif
#include <kvm.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/queue.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "../getifstats.h"
#include "../config.h"
struct nlist list[] = {
{"_ifnet"},
{NULL}
};
int
getifstats(const char * ifname, struct ifdata * data)
{
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct ifnethead ifh;
#elif defined(__OpenBSD__) || defined(__NetBSD__)
struct ifnet_head ifh;
#else
#error "Dont know if I should use struct ifnethead or struct ifnet_head"
#endif
struct ifnet ifc;
struct ifnet *ifp;
kvm_t *kd;
ssize_t n;
char errstr[_POSIX2_LINE_MAX];
#ifdef ENABLE_GETIFSTATS_CACHING
static time_t cache_timestamp = 0;
static struct ifdata cache_data;
time_t current_time;
#endif
if(!data)
return -1;
data->baudrate = 4200000;
data->opackets = 0;
data->ipackets = 0;
data->obytes = 0;
data->ibytes = 0;
if(!ifname || ifname[0]=='\0')
return -1;
#ifdef ENABLE_GETIFSTATS_CACHING
current_time = time(NULL);
if(current_time == ((time_t)-1)) {
syslog(LOG_ERR, "getifstats() : time() error : %m");
} else {
if(current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION) {
memcpy(data, &cache_data, sizeof(struct ifdata));
return 0;
}
}
#endif
/*kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);*/
kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errstr);
if(!kd)
{
syslog (LOG_ERR, "getifstats() : kvm_open(): %s", errstr);
return -1;
}
if(kvm_nlist(kd, list) < 0)
{
syslog(LOG_ERR, "getifstats() : kvm_nlist(): FAILED");
goto error;
}
if(!list[0].n_value)
{
syslog(LOG_ERR, "getifstats() : n_value(): FAILED");
goto error;
}
n = kvm_read(kd, list[0].n_value, &ifh, sizeof(ifh));
if(n<0)
{
syslog(LOG_ERR, "getifstats() : kvm_read(head): %s", kvm_geterr(kd));
goto error;
}
for(ifp = TAILQ_FIRST(&ifh); ifp; ifp = TAILQ_NEXT(&ifc, if_list))
{
n = kvm_read(kd, (u_long)ifp, &ifc, sizeof(ifc));
if(n<0)
{
syslog(LOG_ERR, "getifstats() : kvm_read(element): %s", kvm_geterr(kd));
goto error;
}
if(strcmp(ifname, ifc.if_xname) == 0)
{
/* found the right interface */
data->opackets = ifc.if_data.ifi_opackets;
data->ipackets = ifc.if_data.ifi_ipackets;
data->obytes = ifc.if_data.ifi_obytes;
data->ibytes = ifc.if_data.ifi_ibytes;
data->baudrate = ifc.if_data.ifi_baudrate;
kvm_close(kd);
#ifdef ENABLE_GETIFSTATS_CACHING
if(current_time!=((time_t)-1)) {
cache_timestamp = current_time;
memcpy(&cache_data, data, sizeof(struct ifdata));
}
#endif
return 0; /* ok */
}
}
error:
kvm_close(kd);
return -1; /* not found or error */
}

View File

@ -0,0 +1,116 @@
/* $Id: ifacewatcher.c,v 1.3 2011/06/04 16:19:51 nanard Exp $ */
/* Project MiniUPnP
* web : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas BERNARD
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include "../config.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <syslog.h>
#if !defined(SA_LEN)
#define SA_LEN(sa) (sa)->sa_len
#endif
#define SALIGN (sizeof(long) - 1)
#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
#include "../upnputils.h"
#include "../upnpglobalvars.h"
extern volatile int should_send_public_address_change_notif;
int
OpenAndConfInterfaceWatchSocket(void)
{
int s;
/*s = socket(PF_ROUTE, SOCK_RAW, AF_INET);*/
s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
/* The family parameter may be AF_UNSPEC which will provide routing informa-
* tion for all address families, or can be restricted to a specific address
* family by specifying which one is desired. There can be more than one
* routing socket open per system. */
if(s < 0) {
syslog(LOG_ERR, "OpenAndConfInterfaceWatchSocket socket: %m");
}
return s;
}
void
ProcessInterfaceWatchNotify(int s)
{
char buf[4096];
ssize_t len;
char tmp[64];
struct rt_msghdr * rtm;
struct if_msghdr * ifm;
struct ifa_msghdr * ifam;
#ifdef RTM_IFANNOUNCE
struct if_announcemsghdr * ifanm;
#endif
char * p;
struct sockaddr * sa;
unsigned int ext_if_name_index = 0;
len = recv(s, buf, sizeof(buf), 0);
if(len < 0) {
syslog(LOG_ERR, "ProcessInterfaceWatchNotify recv: %m");
return;
}
if(ext_if_name) {
ext_if_name_index = if_nametoindex(ext_if_name);
}
rtm = (struct rt_msghdr *)buf;
syslog(LOG_DEBUG, "%u rt_msg : msglen=%d version=%d type=%d", (unsigned)len,
rtm->rtm_msglen, rtm->rtm_version, rtm->rtm_type);
switch(rtm->rtm_type) {
case RTM_IFINFO: /* iface going up/down etc. */
ifm = (struct if_msghdr *)buf;
syslog(LOG_DEBUG, " RTM_IFINFO: addrs=%x flags=%x index=%hu",
ifm->ifm_addrs, ifm->ifm_flags, ifm->ifm_index);
break;
#ifdef RTM_IFANNOUNCE
case RTM_IFANNOUNCE: /* iface arrival/departure */
ifanm = (struct if_announcemsghdr *)buf;
syslog(LOG_DEBUG, " RTM_IFANNOUNCE: index=%hu what=%hu ifname=%s",
ifanm->ifan_index, ifanm->ifan_what, ifanm->ifan_name);
break;
#endif
#ifdef RTM_IEEE80211
case RTM_IEEE80211: /* IEEE80211 wireless event */
syslog(LOG_DEBUG, " RTM_IEEE80211");
break;
#endif
case RTM_NEWADDR: /* address being added to iface */
ifam = (struct ifa_msghdr *)buf;
syslog(LOG_DEBUG, " RTM_NEWADDR: addrs=%x flags=%x index=%hu",
ifam->ifam_addrs, ifam->ifam_flags, ifam->ifam_index);
p = buf + sizeof(struct ifa_msghdr);
while(p < buf + len) {
sa = (struct sockaddr *)p;
sockaddr_to_string(sa, tmp, sizeof(tmp));
syslog(LOG_DEBUG, " %s", tmp);
p += SA_RLEN(sa);
}
if(ifam->ifam_index == ext_if_name_index) {
should_send_public_address_change_notif = 1;
}
break;
case RTM_DELADDR: /* address being removed from iface */
ifam = (struct ifa_msghdr *)buf;
if(ifam->ifam_index == ext_if_name_index) {
should_send_public_address_change_notif = 1;
}
break;
default:
syslog(LOG_DEBUG, "unprocessed RTM message type=%d", rtm->rtm_type);
}
}

View File

@ -0,0 +1,32 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include "../getifstats.h"
int
main(int argc, char * * argv)
{
int r;
struct ifdata data;
printf("usage: %s if_name\n", argv[0]);
if(argc<2)
return -1;
r = getifstats(argv[1], &data);
if(r<0)
printf("getifstats() failed\n");
else
{
printf("ipackets = %10lu opackets = %10lu\n",
data.ipackets, data.opackets);
printf("ibytes = %10lu obytes = %10lu\n",
data.ibytes, data.obytes);
printf("baudrate = %10lu\n", data.baudrate);
}
return 0;
}

View File

@ -0,0 +1,32 @@
/* $Id: testifacewatcher.c,v 1.1 2011/05/20 09:34:25 nanard Exp $ */
#include <syslog.h>
int
OpenAndConfInterfaceWatchSocket(void);
void
ProcessInterfaceWatchNotify(int s);
const char * ext_if_name;
volatile int should_send_public_address_change_notif = 0;
int main(int argc, char * * argv)
{
int s;
ext_if_name = "ep0";
openlog("testifacewatcher", LOG_CONS|LOG_PERROR, LOG_USER);
syslog(LOG_DEBUG, "test");
s = OpenAndConfInterfaceWatchSocket();
for(;;) {
if(should_send_public_address_change_notif) {
syslog(LOG_DEBUG, "should_send_public_address_change_notif !");
should_send_public_address_change_notif = 0;
}
ProcessInterfaceWatchNotify(s);
}
closelog();
return 0;
}

531
miniupnpd/bsdqueue.h Normal file
View File

@ -0,0 +1,531 @@
/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 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.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#ifdef QUEUE_MACRO_DEBUG
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#ifdef SLIST_ENTRY
#undef SLIST_ENTRY
#endif
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
for ((varp) = &SLIST_FIRST((head)); \
((var) = *(varp)) != SLIST_END(head); \
(varp) = &SLIST_NEXT((var), field))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_NEXT(head, elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head).cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head).cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

31
miniupnpd/codelength.h Normal file
View File

@ -0,0 +1,31 @@
/* $Id: codelength.h,v 1.3 2011/07/30 13:10:05 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas BERNARD
* copyright (c) 2005-2011 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#ifndef __CODELENGTH_H__
#define __CODELENGTH_H__
/* Encode length by using 7bit per Byte :
* Most significant bit of each byte specifies that the
* following byte is part of the code */
#define DECODELENGTH(n, p) n = 0; \
do { n = (n << 7) | (*p & 0x7f); } \
while((*(p++)&0x80) && (n<(1<<25)));
#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
n = 0; \
do { \
if((p) >= (p_limit)) break; \
n = (n << 7) | (*(p) & 0x7f); \
} while((*((p)++)&0x80) && (n<(1<<25)));
#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
if(n>=16384) *(p++) = (n >> 14) | 0x80; \
if(n>=128) *(p++) = (n >> 7) | 0x80; \
*(p++) = n & 0x7f;
#endif

46
miniupnpd/commonrdr.h Normal file
View File

@ -0,0 +1,46 @@
/* $Id: commonrdr.h,v 1.7 2011/06/22 20:34:39 nanard Exp $ */
/* MiniUPnP project
* (c) 2006-2011 Thomas Bernard
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __COMMONRDR_H__
#define __COMMONRDR_H__
#include "config.h"
/* init and shutdown functions */
int
init_redirect(void);
void
shutdown_redirect(void);
/* get_redirect_rule() gets internal IP and port from
* interface, external port and protocl
*/
int
get_redirect_rule(const char * ifname, unsigned short eport, int proto,
char * iaddr, int iaddrlen, unsigned short * iport,
char * desc, int desclen,
char * rhost, int rhostlen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes);
int
get_redirect_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
char * rhost, int rhostlen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes);
/* return an (malloc'ed) array of "external" port for which there is
* a port mapping. number is the size of the array */
unsigned short *
get_portmappings_in_range(unsigned short startport, unsigned short endport,
int proto, unsigned int * number);
#endif

129
miniupnpd/daemonize.c Normal file
View File

@ -0,0 +1,129 @@
/* $Id: daemonize.c,v 1.12 2011/05/27 09:35:02 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <signal.h>
#include "daemonize.h"
#include "config.h"
#ifndef USE_DAEMON
int
daemonize(void)
{
int pid, i;
switch(fork())
{
/* fork error */
case -1:
perror("fork()");
exit(1);
/* child process */
case 0:
/* obtain a new process group */
if( (pid = setsid()) < 0)
{
perror("setsid()");
exit(1);
}
/* close all descriptors */
for (i=getdtablesize();i>=0;--i) close(i);
i = open("/dev/null", O_RDWR); /* open stdin */
dup(i); /* stdout */
dup(i); /* stderr */
umask(027);
chdir("/"); /* chdir to /tmp ? */
return pid;
/* parent process */
default:
exit(0);
}
}
#endif
int
writepidfile(const char * fname, int pid)
{
char pidstring[16];
int pidstringlen;
int pidfile;
if(!fname || (strlen(fname) == 0))
return -1;
if( (pidfile = open(fname, O_WRONLY|O_CREAT, 0644)) < 0)
{
syslog(LOG_ERR, "Unable to open pidfile for writing %s: %m", fname);
return -1;
}
pidstringlen = snprintf(pidstring, sizeof(pidstring), "%d\n", pid);
if(pidstringlen <= 0)
{
syslog(LOG_ERR,
"Unable to write to pidfile %s: snprintf(): FAILED", fname);
close(pidfile);
return -1;
}
else
{
if(write(pidfile, pidstring, pidstringlen) < 0)
syslog(LOG_ERR, "Unable to write to pidfile %s: %m", fname);
}
close(pidfile);
return 0;
}
int
checkforrunning(const char * fname)
{
char buffer[64];
int pidfile;
pid_t pid;
if(!fname || (strlen(fname) == 0))
return -1;
if( (pidfile = open(fname, O_RDONLY)) < 0)
return 0;
memset(buffer, 0, 64);
if(read(pidfile, buffer, 63))
{
if( (pid = atol(buffer)) > 0)
{
if(!kill(pid, 0))
{
close(pidfile);
return -2;
}
}
}
close(pidfile);
return 0;
}

35
miniupnpd/daemonize.h Normal file
View File

@ -0,0 +1,35 @@
/* $Id: daemonize.h,v 1.6 2008/01/29 13:04:46 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __DAEMONIZE_H__
#define __DAEMONIZE_H__
#include "config.h"
#ifndef USE_DAEMON
/* daemonize()
* "fork" to background, detach from terminal, etc...
* returns: pid of the daemon, exits upon failure */
int
daemonize(void);
#endif
/* writepidfile()
* write the pid to a file */
int
writepidfile(const char * fname, int pid);
/* checkforrunning()
* check for another instance running
* returns: 0 only instance
* -1 invalid filename
* -2 another instance running */
int
checkforrunning(const char * fname);
#endif

324
miniupnpd/genconfig.sh Executable file
View File

@ -0,0 +1,324 @@
#! /bin/sh
# $Id: genconfig.sh,v 1.50 2011/07/25 16:03:46 nanard Exp $
# miniupnp daemon
# http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/
# (c) 2006-2011 Thomas Bernard
# This software is subject to the conditions detailed in the
# LICENCE file provided within the distribution
RM="rm -f"
CONFIGFILE="config.h"
CONFIGMACRO="__CONFIG_H__"
# version reported in XML descriptions
#UPNP_VERSION=20070827
UPNP_VERSION=`date +"%Y%m%d"`
# Facility to syslog
LOG_MINIUPNPD="LOG_DAEMON"
# detecting the OS name and version
OS_NAME=`uname -s`
OS_VERSION=`uname -r`
# pfSense special case
if [ -f /etc/platform ]; then
if [ `cat /etc/platform` = "pfSense" ]; then
OS_NAME=pfSense
OS_VERSION=`cat /etc/version`
fi
fi
# OpenWRT special case
if [ -f ./os.openwrt ]; then
OS_NAME=OpenWRT
OS_VERSION=$(cat ./os.openwrt)
fi
${RM} ${CONFIGFILE}
echo "/* MiniUPnP Project" >> ${CONFIGFILE}
echo " * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/" >> ${CONFIGFILE}
echo " * (c) 2006-2011 Thomas Bernard" >> ${CONFIGFILE}
echo " * generated by $0 on `date` */" >> ${CONFIGFILE}
echo "#ifndef $CONFIGMACRO" >> ${CONFIGFILE}
echo "#define $CONFIGMACRO" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "#include <inttypes.h>" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "#define MINIUPNPD_VERSION \"`cat VERSION`\"" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "#define UPNP_VERSION \"$UPNP_VERSION\"" >> ${CONFIGFILE}
# OS Specific stuff
case $OS_NAME in
OpenBSD)
MAJORVER=`echo $OS_VERSION | cut -d. -f1`
MINORVER=`echo $OS_VERSION | cut -d. -f2`
#echo "OpenBSD majorversion=$MAJORVER minorversion=$MINORVER"
# rtableid was introduced in OpenBSD 4.0
if [ $MAJORVER -ge 4 ]; then
echo "#define PFRULE_HAS_RTABLEID" >> ${CONFIGFILE}
fi
# from the 3.8 version, packets and bytes counters are double : in/out
if [ \( $MAJORVER -ge 4 \) -o \( $MAJORVER -eq 3 -a $MINORVER -ge 8 \) ]; then
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
fi
# from the 4.7 version, new pf
if [ \( $MAJORVER -ge 5 \) -o \( $MAJORVER -eq 4 -a $MINORVER -ge 7 \) ]; then
echo "#define PF_NEWSTYLE" >> ${CONFIGFILE}
fi
echo "#define USE_PF 1" >> ${CONFIGFILE}
FW=pf
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
OS_URL=http://www.openbsd.org/
;;
FreeBSD)
VER=`grep '#define __FreeBSD_version' /usr/include/sys/param.h | awk '{print $3}'`
if [ $VER -ge 700049 ]; then
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
fi
# new way to see which one to use PF or IPF.
# see http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=957
# source file with handy subroutines like checkyesno
. /etc/rc.subr
# source config file so we can probe vars
. /etc/rc.conf
if checkyesno ipfilter_enable; then
echo "Using ipf"
FW=ipf
echo "#define USE_IPF 1" >> ${CONFIGFILE}
elif checkyesno pf_enable; then
echo "Using pf"
FW=pf
echo "#define USE_PF 1" >> ${CONFIGFILE}
# TODO : Add support for IPFW
# echo "#define USE_IPFW 1" >> ${CONFIGFILE}
# FW=ipfw
else
echo "Could not detect usage of ipf or pf. Compiling for pf by default"
FW=pf
echo "#define USE_PF 1" >> ${CONFIGFILE}
fi
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
OS_URL=http://www.freebsd.org/
;;
pfSense)
# we need to detect if PFRULE_INOUT_COUNTS macro is needed
echo "#define USE_PF 1" >> ${CONFIGFILE}
FW=pf
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
OS_URL=http://www.pfsense.com/
;;
NetBSD)
# source file with handy subroutines like checkyesno
. /etc/rc.subr
# source config file so we can probe vars
. /etc/rc.conf
if checkyesno pf; then
echo "#define USE_PF 1" >> ${CONFIGFILE}
FW=pf
elif checkyesno ipfilter; then
echo "#define USE_IPF 1" >> ${CONFIGFILE}
FW=ipf
else
echo "Could not detect ipf nor pf, defaulting to pf."
echo "#define USE_PF 1" >> ${CONFIGFILE}
FW=pf
fi
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
OS_URL=http://www.netbsd.org/
;;
DragonFly)
# source file with handy subroutines like checkyesno
. /etc/rc.subr
# source config file so we can probe vars
. /etc/rc.conf
if checkyesno pf; then
echo "#define USE_PF 1" >> ${CONFIGFILE}
FW=pf
elif checkyesno ipfilter; then
echo "#define USE_IPF 1" >> ${CONFIGFILE}
FW=ipf
else
echo "Could not detect ipf nor pf, defaulting to pf."
echo "#define USE_PF 1" >> ${CONFIGFILE}
FW=pf
fi
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
OS_URL=http://www.dragonflybsd.org/
;;
SunOS)
echo "#define USE_IPF 1" >> ${CONFIGFILE}
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
FW=ipf
echo "#define LOG_PERROR 0" >> ${CONFIGFILE}
echo "#define SOLARIS_KSTATS 1" >> ${CONFIGFILE}
OS_URL=http://www.sun.com/solaris/
;;
Linux)
OS_URL=http://www.kernel.org/
KERNVERA=`echo $OS_VERSION | awk -F. '{print $1}'`
KERNVERB=`echo $OS_VERSION | awk -F. '{print $2}'`
KERNVERC=`echo $OS_VERSION | awk -F. '{print $3}'`
KERNVERD=`echo $OS_VERSION | awk -F. '{print $4}'`
#echo "$KERNVERA.$KERNVERB.$KERNVERC.$KERNVERD"
# Debian GNU/Linux special case
if [ -f /etc/debian_version ]; then
OS_NAME=Debian
OS_VERSION=`cat /etc/debian_version`
OS_URL=http://www.debian.org/
fi
# same thing for Gentoo linux
if [ -f /etc/gentoo-release ]; then
OS_NAME=Gentoo
OS_VERSION=`cat /etc/gentoo-release`
OS_URL=http://www.gentoo.org/
fi
# use lsb_release (Linux Standard Base) when available
LSB_RELEASE=`which lsb_release`
if [ 0 -eq $? ]; then
OS_NAME=`${LSB_RELEASE} -i -s`
OS_VERSION=`${LSB_RELEASE} -r -s`
case $OS_NAME in
Debian)
OS_URL=http://www.debian.org/
OS_VERSION=`${LSB_RELEASE} -c -s`
;;
Ubuntu)
OS_URL=http://www.ubuntu.com/
OS_VERSION=`${LSB_RELEASE} -c -s`
;;
Gentoo)
OS_URL=http://www.gentoo.org/
;;
esac
fi
echo "#define USE_NETFILTER 1" >> ${CONFIGFILE}
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
FW=netfilter
;;
OpenWRT)
OS_URL=http://www.openwrt.org/
echo "#define USE_NETFILTER 1" >> ${CONFIGFILE}
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
FW=netfilter
;;
Darwin)
echo "#define USE_IPFW 1" >> ${CONFIGFILE}
echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE}
FW=ipfw
OS_URL=http://developer.apple.com/macosx
;;
*)
echo "Unknown OS : $OS_NAME"
echo "Please contact the author at http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/."
exit 1
;;
esac
echo "Configuring compilation for [$OS_NAME] [$OS_VERSION] with [$FW] firewall software."
echo "Please edit config.h for more compilation options."
# define SUPPORT_REMOTEHOST if the FW related code really supports setting
# a RemoteHost
if [ \( "$FW" = "netfilter" \) -o \( "$FW" = "pf" \) -o \( "$FW" = "ipfw" \) ] ; then
echo "#define SUPPORT_REMOTEHOST" >> ${CONFIGFILE}
fi
echo "" >> ${CONFIGFILE}
echo "#define OS_NAME \"$OS_NAME\"" >> ${CONFIGFILE}
echo "#define OS_VERSION \"$OS_NAME/$OS_VERSION\"" >> ${CONFIGFILE}
echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE}
echo "#define LOG_MINIUPNPD ${LOG_MINIUPNPD}" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Uncomment the following line to allow miniupnpd to be" >> ${CONFIGFILE}
echo " * controlled by miniupnpdctl */" >> ${CONFIGFILE}
echo "/*#define USE_MINIUPNPDCTL*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFIGFILE}
echo "#define ENABLE_NATPMP" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Uncomment the following line to enable generation of" >> ${CONFIGFILE}
echo " * filter rules with pf */" >> ${CONFIGFILE}
echo "/*#define PF_ENABLE_FILTER_RULES*/">> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Uncomment the following line to enable caching of results of" >> ${CONFIGFILE}
echo " * the getifstats() function */" >> ${CONFIGFILE}
echo "/*#define ENABLE_GETIFSTATS_CACHING*/" >> ${CONFIGFILE}
echo "/* The cache duration is indicated in seconds */" >> ${CONFIGFILE}
echo "#define GETIFSTATS_CACHING_DURATION 2" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Uncomment the following line to enable multiple external ip support */" >> ${CONFIGFILE}
echo "/* note : That is EXPERIMENTAL, do not use that unless you know perfectly what you are doing */" >> ${CONFIGFILE}
echo "/* Dynamic external ip adresses are not supported when this option is enabled." >> ${CONFIGFILE}
echo " * Also note that you would need to configure your .conf file accordingly. */" >> ${CONFIGFILE}
echo "/*#define MULTIPLE_EXTERNAL_IP*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Comment the following line to use home made daemonize() func instead" >> ${CONFIGFILE}
echo " * of BSD daemon() */" >> ${CONFIGFILE}
echo "#define USE_DAEMON" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Uncomment the following line to enable lease file support */" >> ${CONFIGFILE}
echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Define one or none of the two following macros in order to make some" >> ${CONFIGFILE}
echo " * clients happy. It will change the XML Root Description of the IGD." >> ${CONFIGFILE}
echo " * Enabling the Layer3Forwarding Service seems to be the more compatible" >> ${CONFIGFILE}
echo " * option. */" >> ${CONFIGFILE}
echo "/*#define HAS_DUMMY_SERVICE*/" >> ${CONFIGFILE}
echo "#define ENABLE_L3F_SERVICE" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Enable IP v6 support */" >> ${CONFIGFILE}
echo "/*#define ENABLE_IPV6*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Enable the support of IGD v2 specification." >> ${CONFIGFILE}
echo " * This is not fully tested yet and can cause incompatibilities with some" >> ${CONFIGFILE}
echo " * control points, so enable with care. */" >> ${CONFIGFILE}
echo "/*#define IGD_V2*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "#ifdef IGD_V2" >> ${CONFIGFILE}
echo "/* Enable DeviceProtection service (IGDv2) */" >> ${CONFIGFILE}
echo "#define ENABLE_DP_SERVICE" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Enable WANIPv6FirewallControl service (IGDv2). needs IPv6 */" >> ${CONFIGFILE}
echo "#ifdef ENABLE_IPV6" >> ${CONFIGFILE}
echo "#define ENABLE_6FC_SERVICE" >> ${CONFIGFILE}
echo "#endif /* ENABLE_IPV6 */" >> ${CONFIGFILE}
echo "#endif /* IGD_V2 */" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* UPnP Events support. Working well enough to be enabled by default." >> ${CONFIGFILE}
echo " * It can be disabled to save a few bytes. */" >> ${CONFIGFILE}
echo "#define ENABLE_EVENTS" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* include interface name in pf and ipf rules */" >> ${CONFIGFILE}
echo "#define USE_IFNAME_IN_RULES" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Experimental NFQUEUE support. */" >> ${CONFIGFILE}
echo "/*#define ENABLE_NFQUEUE*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Enable to make MiniUPnPd more strict about UPnP conformance" >> ${CONFIGFILE}
echo " * and the messages it receives from control points */" >> ${CONFIGFILE}
echo "/*#define UPNP_STRICT*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "#endif" >> ${CONFIGFILE}
exit 0

74
miniupnpd/getconnstatus.c Normal file
View File

@ -0,0 +1,74 @@
/* $Id: getconnstatus.c,v 1.4 2011/05/23 20:22:41 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "getconnstatus.h"
#include "getifaddr.h"
#define STATUS_UNCONFIGURED (0)
#define STATUS_CONNECTING (1)
#define STATUS_CONNECTED (2)
#define STATUS_PENDINGDISCONNECT (3)
#define STATUS_DISCONNECTING (4)
#define STATUS_DISCONNECTED (5)
/**
* get the connection status
* return values :
* 0 - Unconfigured
* 1 - Connecting
* 2 - Connected
* 3 - PendingDisconnect
* 4 - Disconnecting
* 5 - Disconnected */
int
get_wan_connection_status(const char * ifname)
{
char addr[INET_ADDRSTRLEN];
int r;
/* we need a better implementation here.
* I'm afraid it should be device specific */
r = getifaddr(ifname, addr, INET_ADDRSTRLEN);
return (r < 0) ? STATUS_DISCONNECTED : STATUS_CONNECTED;
}
/**
* return the same value as get_wan_connection_status()
* as a C string */
const char *
get_wan_connection_status_str(const char * ifname)
{
int status;
const char * str = NULL;
status = get_wan_connection_status(ifname);
switch(status) {
case 0:
str = "Unconfigured";
break;
case 1:
str = "Connecting";
break;
case 2:
str = "Connected";
break;
case 3:
str = "PendingDisconnect";
break;
case 4:
str = "Disconnecting";
break;
case 5:
str = "Disconnected";
break;
}
return str;
}

30
miniupnpd/getconnstatus.h Normal file
View File

@ -0,0 +1,30 @@
/* $Id: getconnstatus.h,v 1.2 2011/05/23 20:22:41 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __GETCONNSTATUS_H__
#define __GETCONNSTATUS_H__
/**
* get the connection status
* return values :
* 0 - Unconfigured
* 1 - Connecting
* 2 - Connected
* 3 - PendingDisconnect
* 4 - Disconnecting
* 5 - Disconnected */
int
get_wan_connection_status(const char * ifname);
/**
* return the same value as get_wan_connection_status()
* as a C string */
const char *
get_wan_connection_status_str(const char * ifname);
#endif

142
miniupnpd/getifaddr.c Normal file
View File

@ -0,0 +1,142 @@
/* $Id: getifaddr.c,v 1.11 2011/05/15 08:59:27 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#if defined(sun)
#include <sys/sockio.h>
#endif
#include "config.h"
#include "getifaddr.h"
#if defined(USE_GETIFADDRS) || defined(ENABLE_IPV6)
#include <ifaddrs.h>
#endif
int
getifaddr(const char * ifname, char * buf, int len)
{
#ifndef USE_GETIFADDRS
/* use ioctl SIOCGIFADDR. Works only for ip v4 */
/* SIOCGIFADDR struct ifreq * */
int s;
struct ifreq ifr;
int ifrlen;
struct sockaddr_in * addr;
ifrlen = sizeof(ifr);
if(!ifname || ifname[0]=='\0')
return -1;
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
{
syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
return -1;
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m");
close(s);
return -1;
}
addr = (struct sockaddr_in *)&ifr.ifr_addr;
if(!inet_ntop(AF_INET, &addr->sin_addr, buf, len))
{
syslog(LOG_ERR, "inet_ntop(): %m");
close(s);
return -1;
}
close(s);
#else /* ifndef USE_GETIFADDRS */
/* Works for all address families (both ip v4 and ip v6) */
struct ifaddrs * ifap;
struct ifaddrs * ife;
if(!ifname || ifname[0]=='\0')
return -1;
if(getifaddrs(&ifap)<0)
{
syslog(LOG_ERR, "getifaddrs: %m");
return -1;
}
for(ife = ifap; ife; ife = ife->ifa_next)
{
/* skip other interfaces */
if(0 != strcmp(ifname, ife->ifa_name))
continue;
switch(ife->ifa_addr->sa_family)
{
case AF_INET:
inet_ntop(ife->ifa_addr->sa_family,
&((struct sockaddr_in *)ife->ifa_addr)->sin_addr,
buf, len);
break;
/*
case AF_INET6:
inet_ntop(ife->ifa_addr->sa_family,
&((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr,
buf, len);
*/
}
}
freeifaddrs(ifap);
#endif
return 0;
}
#ifdef ENABLE_IPV6
int
find_ipv6_addr(const char * ifname,
char * dst, int n)
{
struct ifaddrs * ifap;
struct ifaddrs * ife;
const struct sockaddr_in6 * addr;
char buf[64];
int r = 0;
if(!dst)
return -1;
if(getifaddrs(&ifap)<0)
{
syslog(LOG_ERR, "getifaddrs: %m");
return -1;
}
for(ife = ifap; ife; ife = ife->ifa_next)
{
/* skip other interfaces if one was specified */
if(ifname && (0 != strcmp(ifname, ife->ifa_name)))
continue;
if(ife->ifa_addr->sa_family == AF_INET6)
{
addr = (const struct sockaddr_in6 *)ife->ifa_addr;
if(!IN6_IS_ADDR_LOOPBACK(&addr->sin6_addr)
&& !IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))
{
inet_ntop(ife->ifa_addr->sa_family,
&addr->sin6_addr,
buf, sizeof(buf));
/* add brackets */
snprintf(dst, n, "[%s]", buf);
r = 1;
}
}
}
return r;
}
#endif

25
miniupnpd/getifaddr.h Normal file
View File

@ -0,0 +1,25 @@
/* $Id: getifaddr.h,v 1.5 2011/05/15 08:59:27 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __GETIFADDR_H__
#define __GETIFADDR_H__
/* getifaddr()
* take a network interface name and write the
* ip v4 address as text in the buffer
* returns: 0 success, -1 failure */
int
getifaddr(const char * ifname, char * buf, int len);
/* find a non link local IP v6 address for the interface.
* if ifname is NULL, look for all interfaces */
int
find_ipv6_addr(const char * ifname,
char * dst, int n);
#endif

25
miniupnpd/getifstats.h Normal file
View File

@ -0,0 +1,25 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2008 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __GETIFSTATS_H__
#define __GETIFSTATS_H__
struct ifdata {
unsigned long opackets;
unsigned long ipackets;
unsigned long obytes;
unsigned long ibytes;
unsigned long baudrate;
};
/* getifstats()
* Fill the ifdata structure with statistics for network interface ifname.
* Return 0 in case of success, -1 for bad arguments or any error */
int
getifstats(const char * ifname, struct ifdata * data);
#endif

48
miniupnpd/ifacewatcher.h Normal file
View File

@ -0,0 +1,48 @@
/* $Id: ifacewatcher.h,v 1.2 2011/05/20 09:42:49 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2009 Thomas Bernard
*
* ifacewatcher.h
*
* This file implements dynamic serving of new network interfaces
* which weren't available during daemon start. It also takes care
* of interfaces which become unavailable.
*
* Copyright (c) 2011, Alexey Osipov <simba@lerlan.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * 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.
* * The name of the author may not 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 OWNER 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. */
#ifndef __IFACEWATCHER_H__
#define __IFACEWATCHER_H__
#include "config.h"
#ifdef USE_IFACEWATCHER
int OpenAndConfInterfaceWatchSocket(void);
void ProcessInterfaceWatchNotify(int s);
#endif
#endif

16
miniupnpd/ipf/Makefile Normal file
View File

@ -0,0 +1,16 @@
# $Id: Makefile,v 1.1 2007/09/25 19:44:43 nanard Exp $
CC=gcc
CFLAGS=-Wall -g -I.
all: testipfrdr
clean:
rm *.o testipfrdr
testipfrdr: testipfrdr.o ipfrdr.o
$(CC) -o $@ $^
ipfrdr.o: ipfrdr.c
testipfrdr.o: testipfrdr.c

602
miniupnpd/ipf/ipfrdr.c Normal file
View File

@ -0,0 +1,602 @@
/* $Id: ipfrdr.c,v 1.11 2009/10/10 18:34:39 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2007 Darren Reed
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/file.h>
/*
* This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
* Needed here because on some systems <sys/uio.h> gets included by things
* like <sys/socket.h>
*/
#ifndef _KERNEL
# define ADD_KERNEL
# define _KERNEL
# define KERNEL
#endif
#ifdef __OpenBSD__
struct file;
#endif
#include <sys/uio.h>
#ifdef ADD_KERNEL
# undef _KERNEL
# undef KERNEL
#endif
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <net/if.h>
#if __FreeBSD_version >= 300000
# include <net/if_var.h>
#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#ifndef TCP_PAWS_IDLE /* IRIX */
# include <netinet/tcp.h>
#endif
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <stddef.h>
#include <stdio.h>
#if !defined(__SVR4) && !defined(__svr4__) && defined(sun)
# include <strings.h>
#endif
#include <string.h>
#include <unistd.h>
#include "../config.h"
#include "netinet/ipl.h"
#include "netinet/ip_compat.h"
#include "netinet/ip_fil.h"
#include "netinet/ip_nat.h"
#include "netinet/ip_state.h"
#ifndef __P
# ifdef __STDC__
# define __P(x) x
# else
# define __P(x) ()
# endif
#endif
#ifndef __STDC__
# undef const
# define const
#endif
#ifndef U_32_T
# define U_32_T 1
# if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
defined(__sgi)
typedef u_int32_t u_32_t;
# else
# if defined(__alpha__) || defined(__alpha) || defined(_LP64)
typedef unsigned int u_32_t;
# else
# if SOLARIS2 >= 6
typedef uint32_t u_32_t;
# else
typedef unsigned int u_32_t;
# endif
# endif
# endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */
#endif /* U_32_T */
#if defined(__NetBSD__) || defined(__OpenBSD__) || \
(_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) || \
SOLARIS || defined(__sgi) || defined(__osf__) || defined(linux)
# include <stdarg.h>
typedef int (* ioctlfunc_t) __P((int, ioctlcmd_t, ...));
#else
typedef int (* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *));
#endif
typedef void (* addfunc_t) __P((int, ioctlfunc_t, void *));
typedef int (* copyfunc_t) __P((void *, void *, size_t));
/*
* SunOS4
*/
#if defined(sun) && !defined(__SVR4) && !defined(__svr4__)
extern int ioctl __P((int, int, void *));
#endif
#include "../upnpglobalvars.h"
/* group name */
static const char group_name[] = "miniupnpd";
static int dev = -1;
static int dev_ipl = -1;
/* IPFilter cannot store redirection descriptions, so we use our
* own structure to store them */
struct rdr_desc {
struct rdr_desc * next;
unsigned short eport;
int proto;
char str[];
};
/* pointer to the chained list where descriptions are stored */
static struct rdr_desc * rdr_desc_list;
static void
add_redirect_desc(unsigned short eport, int proto, const char * desc)
{
struct rdr_desc * p;
size_t l;
if (desc != NULL) {
l = strlen(desc) + 1;
p = malloc(sizeof(struct rdr_desc) + l);
if (p) {
p->next = rdr_desc_list;
p->eport = eport;
p->proto = proto;
memcpy(p->str, desc, l);
rdr_desc_list = p;
}
}
}
static void
del_redirect_desc(unsigned short eport, int proto)
{
struct rdr_desc * p, * last;
last = NULL;
for (p = rdr_desc_list; p; p = p->next) {
if(p->eport == eport && p->proto == proto) {
if (last == NULL)
rdr_desc_list = p->next;
else
last->next = p->next;
free(p);
return;
}
}
}
static void
get_redirect_desc(unsigned short eport, int proto, char * desc, int desclen)
{
struct rdr_desc * p;
if (desc == NULL || desclen == 0)
return;
for (p = rdr_desc_list; p; p = p->next) {
if (p->eport == eport && p->proto == proto)
{
strncpy(desc, p->str, desclen);
return;
}
}
}
int init_redirect(void)
{
dev = open(IPNAT_NAME, O_RDWR);
if (dev < 0) {
syslog(LOG_ERR, "open(\"%s\"): %m", IPNAT_NAME);
return -1;
}
dev_ipl = open(IPL_NAME, O_RDWR);
if (dev_ipl < 0) {
syslog(LOG_ERR, "open(\"%s\"): %m", IPL_NAME);
return -1;
}
return 0;
}
void shutdown_redirect(void)
{
if (dev >= 0) {
close(dev);
dev = -1;
}
if (dev_ipl >= 0) {
close(dev_ipl);
dev = -1;
}
return;
}
int
add_redirect_rule2(const char * ifname, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc)
{
struct ipnat ipnat;
struct ipfobj obj;
int r;
if (dev < 0) {
syslog(LOG_ERR, "%s not open", IPNAT_NAME);
return -1;
}
memset(&obj, 0, sizeof(obj));
memset(&ipnat, 0, sizeof(ipnat));
ipnat.in_redir = NAT_REDIRECT;
ipnat.in_p = proto;
if (proto == IPPROTO_TCP)
ipnat.in_flags = IPN_TCP;
if (proto == IPPROTO_UDP)
ipnat.in_flags = IPN_UDP;
ipnat.in_dcmp = FR_EQUAL;
ipnat.in_pmin = htons(eport);
ipnat.in_pmax = htons(eport);
ipnat.in_pnext = htons(iport);
ipnat.in_v = 4;
strlcpy(ipnat.in_tag.ipt_tag, group_name, IPFTAG_LEN);
#ifdef USE_IFNAME_IN_RULES
if (ifname) {
strlcpy(ipnat.in_ifnames[0], ifname, IFNAMSIZ);
strlcpy(ipnat.in_ifnames[1], ifname, IFNAMSIZ);
}
#endif
inet_pton(AF_INET, iaddr, &ipnat.in_in[0].in4);
ipnat.in_in[1].in4.s_addr = 0xffffffff;
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_size = sizeof(ipnat);
obj.ipfo_ptr = &ipnat;
obj.ipfo_type = IPFOBJ_IPNAT;
r = ioctl(dev, SIOCADNAT, &obj);
if (r == -1)
syslog(LOG_ERR, "ioctl(SIOCADNAT): %m");
else
add_redirect_desc(eport, proto, desc);
return r;
}
/* get_redirect_rule()
* return value : 0 success (found)
* -1 = error or rule not found */
int
get_redirect_rule(const char * ifname, unsigned short eport, int proto,
char * iaddr, int iaddrlen, unsigned short * iport,
char * desc, int desclen,
u_int64_t * packets, u_int64_t * bytes)
{
ipfgeniter_t iter;
ipfobj_t obj;
ipnat_t ipn;
int r;
memset(&obj, 0, sizeof(obj));
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_type = IPFOBJ_GENITER;
obj.ipfo_size = sizeof(iter);
obj.ipfo_ptr = &iter;
iter.igi_type = IPFGENITER_IPNAT;
#if IPFILTER_VERSION > 4011300
iter.igi_nitems = 1;
#endif
iter.igi_data = &ipn;
if (dev < 0) {
syslog(LOG_ERR, "%s not open", IPNAT_NAME);
return -1;
}
r = -1;
do {
if (ioctl(dev, SIOCGENITER, &obj) == -1) {
syslog(LOG_ERR, "ioctl(dev, SIOCGENITER): %m");
break;
}
if (eport == ntohs(ipn.in_pmin) &&
eport == ntohs(ipn.in_pmax) &&
strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
ipn.in_p == proto)
{
strlcpy(desc, "", desclen);
if (packets != NULL)
*packets = 0;
if (bytes != NULL)
*bytes = 0;
if (iport != NULL)
*iport = ntohs(ipn.in_pnext);
if (desc != NULL)
get_redirect_desc(eport, proto, desc, desclen);
inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
r = 0;
}
} while (ipn.in_next != NULL);
return r;
}
int
get_redirect_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
u_int64_t * packets, u_int64_t * bytes)
{
ipfgeniter_t iter;
ipfobj_t obj;
ipnat_t ipn;
int n, r;
if (index < 0)
return -1;
if (dev < 0) {
syslog(LOG_ERR, "%s not open", IPNAT_NAME);
return -1;
}
memset(&obj, 0, sizeof(obj));
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_ptr = &iter;
obj.ipfo_size = sizeof(iter);
obj.ipfo_type = IPFOBJ_GENITER;
iter.igi_type = IPFGENITER_IPNAT;
#if IPFILTER_VERSION > 4011300
iter.igi_nitems = 1;
#endif
iter.igi_data = &ipn;
n = 0;
r = -1;
do {
if (ioctl(dev, SIOCGENITER, &obj) == -1) {
syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
"get_redirect_rule_by_index");
break;
}
if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0)
continue;
if (index == n++) {
*proto = ipn.in_p;
*eport = ntohs(ipn.in_pmax);
*iport = ntohs(ipn.in_pnext);
if (ifname)
strlcpy(ifname, ipn.in_ifnames[0], IFNAMSIZ);
if (packets != NULL)
*packets = 0;
if (bytes != NULL)
*bytes = 0;
if (desc != NULL)
get_redirect_desc(*eport, *proto, desc, desclen);
inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
r = 0;
}
} while (ipn.in_next != NULL);
return r;
}
static int
real_delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
{
ipfgeniter_t iter;
ipfobj_t obj;
ipnat_t ipn;
int r;
memset(&obj, 0, sizeof(obj));
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_type = IPFOBJ_GENITER;
obj.ipfo_size = sizeof(iter);
obj.ipfo_ptr = &iter;
iter.igi_type = IPFGENITER_IPNAT;
#if IPFILTER_VERSION > 4011300
iter.igi_nitems = 1;
#endif
iter.igi_data = &ipn;
if (dev < 0) {
syslog(LOG_ERR, "%s not open", IPNAT_NAME);
return -1;
}
r = -1;
do {
if (ioctl(dev, SIOCGENITER, &obj) == -1) {
syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
"delete_redirect_rule");
break;
}
if (eport == ntohs(ipn.in_pmin) &&
eport == ntohs(ipn.in_pmax) &&
strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
ipn.in_p == proto)
{
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_size = sizeof(ipn);
obj.ipfo_ptr = &ipn;
obj.ipfo_type = IPFOBJ_IPNAT;
r = ioctl(dev, SIOCRMNAT, &obj);
if (r == -1)
syslog(LOG_ERR, "%s:ioctl(SIOCRMNAT): %m",
"delete_redirect_rule");
/* Delete the desc even if the above failed */
del_redirect_desc(eport, proto);
break;
}
} while (ipn.in_next != NULL);
return r;
}
/* FIXME: For some reason, the iter isn't reset every other delete,
* so we attempt 2 deletes. */
int
delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
{
int r;
r = real_delete_redirect_rule(ifname, eport, proto);
if (r == -1)
r = real_delete_redirect_rule(ifname, eport, proto);
return r;
}
/* thanks to Seth Mos for this function */
int
add_filter_rule2(const char * ifname, const char * iaddr,
unsigned short eport, unsigned short iport,
int proto, const char * desc)
{
ipfobj_t obj;
frentry_t fr;
fripf_t ipffr;
int r;
if (dev_ipl < 0) {
syslog(LOG_ERR, "%s not open", IPL_NAME);
return -1;
}
memset(&obj, 0, sizeof(obj));
memset(&fr, 0, sizeof(fr));
memset(&ipffr, 0, sizeof(ipffr));
fr.fr_flags = FR_PASS|FR_KEEPSTATE|FR_QUICK|FR_INQUE;
if (GETFLAG(LOGPACKETSMASK))
fr.fr_flags |= FR_LOG|FR_LOGFIRST;
fr.fr_v = 4;
fr.fr_type = FR_T_IPF;
fr.fr_dun.fru_ipf = &ipffr;
fr.fr_dsize = sizeof(ipffr);
fr.fr_isc = (void *)-1;
fr.fr_proto = proto;
fr.fr_mproto = 0xff;
fr.fr_dcmp = FR_EQUAL;
fr.fr_dport = eport;
#ifdef USE_IFNAME_IN_RULES
if (ifname)
strlcpy(fr.fr_ifnames[0], ifname, IFNAMSIZ);
#endif
strlcpy(fr.fr_group, group_name, sizeof(fr.fr_group));
if (proto == IPPROTO_TCP) {
fr.fr_tcpf = TH_SYN;
fr.fr_tcpfm = TH_SYN|TH_ACK|TH_RST|TH_FIN|TH_URG|TH_PUSH;
}
inet_pton(AF_INET, iaddr, &fr.fr_daddr);
fr.fr_dmask = 0xffffffff;
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_ptr = &fr;
obj.ipfo_size = sizeof(fr);
r = ioctl(dev_ipl, SIOCINAFR, &obj);
if (r == -1) {
if (errno == ESRCH)
syslog(LOG_ERR,
"SIOCINAFR(missing 'head %s' rule?):%m",
group_name);
else
syslog(LOG_ERR, "SIOCINAFR:%m");
}
return r;
}
int
delete_filter_rule(const char * ifname, unsigned short eport, int proto)
{
ipfobj_t wobj, dobj;
ipfruleiter_t rule;
u_long darray[1000];
u_long array[1000];
friostat_t fio;
frentry_t *fp;
int r;
if (dev_ipl < 0) {
syslog(LOG_ERR, "%s not open", IPL_NAME);
return -1;
}
wobj.ipfo_rev = IPFILTER_VERSION;
wobj.ipfo_type = IPFOBJ_IPFSTAT;
wobj.ipfo_size = sizeof(fio);
wobj.ipfo_ptr = &fio;
if (ioctl(dev_ipl, SIOCGETFS, &wobj) == -1) {
syslog(LOG_ERR, "ioctl(SIOCGETFS): %m");
return -1;
}
wobj.ipfo_rev = IPFILTER_VERSION;
wobj.ipfo_ptr = &rule;
wobj.ipfo_size = sizeof(rule);
wobj.ipfo_type = IPFOBJ_IPFITER;
fp = (frentry_t *)array;
fp->fr_dun.fru_data = darray;
fp->fr_dsize = sizeof(darray);
rule.iri_inout = 0;
rule.iri_active = fio.f_active;
#if IPFILTER_VERSION > 4011300
rule.iri_nrules = 1;
rule.iri_v = 4;
#endif
rule.iri_rule = fp;
strlcpy(rule.iri_group, group_name, sizeof(rule.iri_group));
dobj.ipfo_rev = IPFILTER_VERSION;
dobj.ipfo_size = sizeof(*fp);
dobj.ipfo_type = IPFOBJ_FRENTRY;
r = -1;
do {
memset(array, 0xff, sizeof(array));
if (ioctl(dev_ipl, SIOCIPFITER, &wobj) == -1) {
syslog(LOG_ERR, "ioctl(SIOCIPFITER): %m");
break;
}
if (fp->fr_data != NULL)
fp->fr_data = (char *)fp + sizeof(*fp);
if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF &&
fp->fr_dport == eport &&
fp->fr_proto == proto)
{
dobj.ipfo_ptr = fp;
r = ioctl(dev_ipl, SIOCRMAFR, &dobj);
if (r == -1)
syslog(LOG_ERR, "ioctl(SIOCRMAFR): %m");
break;
}
} while (fp->fr_next != NULL);
return r;
}

55
miniupnpd/ipf/ipfrdr.h Normal file
View File

@ -0,0 +1,55 @@
/* $Id: ipfrdr.h,v 1.3 2007/11/02 22:54:01 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2007 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __IPFRDR_H__
#define __IPFRDR_H__
#include "../commonrdr.h"
int
add_redirect_rule2(const char * ifname, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc);
int
add_filter_rule2(const char * ifname, const char * iaddr,
unsigned short eport, unsigned short iport,
int proto, const char * desc);
/* get_redirect_rule() gets internal IP and port from
* interface, external port and protocl
*/
#if 0
int
get_redirect_rule(const char * ifname, unsigned short eport, int proto,
char * iaddr, int iaddrlen, unsigned short * iport,
char * desc, int desclen,
u_int64_t * packets, u_int64_t * bytes);
int
get_redirect_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
u_int64_t * packets, u_int64_t * bytes);
#endif
/* delete_redirect_rule()
*/
int
delete_redirect_rule(const char * ifname, unsigned short eport, int proto);
/* delete_filter_rule()
*/
int
delete_filter_rule(const char * ifname, unsigned short eport, int proto);
int
clear_redirect_rules(void);
#endif

View File

@ -0,0 +1,25 @@
/* $Id: testipfrdr.c,v 1.3 2007/10/01 16:21:23 nanard Exp $ */
#include <stdio.h>
#include <syslog.h>
#include <netinet/in.h>
#include "ipfrdr.h"
extern void
test_list_nat_rules();
/* test program for ipfrdr.c */
int
main(int argc, char * * argv)
{
openlog("testipfrdrd", LOG_CONS|LOG_PERROR, LOG_USER);
printf("List nat rules :\n");
test_list_nat_rules();
printf("Add redirection !\n");
add_redirect_rule2("ep0", 12345, "1.2.3.4", 54321, IPPROTO_UDP,
"redirection description");
printf("List nat rules :\n");
test_list_nat_rules();
return 0;
}

17
miniupnpd/ipfw/Makefile Normal file
View File

@ -0,0 +1,17 @@
# $Id: Makefile,v 1.2 2009/08/20 09:31:10 nanard Exp $
CC=gcc
CFLAGS=-Wall -g -I.
RM=rm -f
all: testipfwrdr
clean:
$(RM) *.o testipfwrdr
testipfwrdr: testipfwrdr.o ipfwrdr.o
$(CC) -o $@ $^
ipfwrdr.o: ipfwrdr.c
testipfwrdr.o: testipfwrdr.c

113
miniupnpd/ipfw/ipfwaux.h Normal file
View File

@ -0,0 +1,113 @@
/* $Id: ipfwaux.h,v 1.3 2011/02/20 23:43:41 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009 Jardel Weyrich
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#ifndef __IPFWAUX_H__
#define __IPFWAUX_H__
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip_fw.h>
#define IP_FW_BASE (IP_FW_ADD - 5)
#define IP_FW_INIT (IP_FW_BASE + 1)
#define IP_FW_TERM (IP_FW_BASE + 2)
static int ipfw_exec(int optname, void * optval, uintptr_t optlen) {
static int sock = -1;
int result;
switch (optname) {
case IP_FW_INIT:
if (sock == -1)
sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (sock < 0) {
syslog(LOG_ERR, "socket(SOCK_RAW): %m");
return -1;
}
break;
case IP_FW_TERM:
if (sock != -1)
close(sock);
sock = -1;
break;
case IP_FW_ADD:
case IP_FW_DEL:
result = setsockopt(sock, IPPROTO_IP, optname, optval, optlen);
if (result == -1) {
syslog(LOG_ERR, "setsockopt(): %m");
return -1;
}
break;
case IP_FW_GET:
result = getsockopt(sock, IPPROTO_IP, optname, optval, (socklen_t *)optlen);
if (result == -1) {
syslog(LOG_ERR, "getsockopt(): %m");
return -1;
}
break;
default:
syslog(LOG_ERR, "unhandled option");
return -1;
}
return 0;
}
static void ipfw_free_ruleset(struct ip_fw ** rules) {
if (rules == NULL || *rules == NULL)
return;
free(*rules);
*rules = NULL;
}
static int ipfw_fetch_ruleset(struct ip_fw ** rules, int * total_fetched, int count) {
int fetched;
socklen_t size;
if (rules == NULL || *total_fetched < 0 || count < 1)
return -1;
size = sizeof(struct ip_fw) * (*total_fetched + count);
*rules = (struct ip_fw *)realloc(*rules, size);
if (*rules == NULL) {
syslog(LOG_ERR, "realloc(): %m");
return -1;
}
(*rules)->version = IP_FW_CURRENT_API_VERSION;
if (ipfw_exec(IP_FW_GET, *rules, (uintptr_t)&size) < 0)
return -1;
fetched = *total_fetched;
*total_fetched = size / sizeof(struct ip_fw);
return *total_fetched - fetched;
}
static int ipfw_validate_protocol(int value) {
switch (value) {
case IPPROTO_TCP:
case IPPROTO_UDP:
break;
default:
syslog(LOG_ERR, "invalid protocol");
return -1;
}
return 0;
}
static int ipfw_validate_ifname(const char * const value) {
int len = strlen(value);
if (len < 2 || len > FW_IFNLEN) {
syslog(LOG_ERR, "invalid interface name");
return -1;
}
return 0;
}
#endif

467
miniupnpd/ipfw/ipfwrdr.c Normal file
View File

@ -0,0 +1,467 @@
/* $Id: ipfwrdr.c,v 1.11 2011/07/12 19:17:05 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009 Jardel Weyrich
* (c) 2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#include "../config.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/file.h>
//
// This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
// Needed here because on some systems <sys/uio.h> gets included by things
// like <sys/socket.h>
//
#ifndef _KERNEL
# define ADD_KERNEL
# define _KERNEL
# define KERNEL
#endif
#ifdef __OpenBSD__
struct file;
#endif
#include <sys/uio.h>
#ifdef ADD_KERNEL
# undef _KERNEL
# undef KERNEL
#endif
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <net/if.h>
#if __FreeBSD_version >= 300000
# include <net/if_var.h>
#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <stddef.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip_fw.h>
#include "ipfwaux.h"
#include "ipfwrdr.h"
#include "../upnpglobalvars.h"
/* init and shutdown functions */
int init_redirect(void) {
return ipfw_exec(IP_FW_INIT, NULL, 0);
}
void shutdown_redirect(void) {
ipfw_exec(IP_FW_TERM, NULL, 0);
}
/* ipfw cannot store descriptions and timestamp for port mappings so we keep
* our own list in memory */
struct mapping_desc_time {
struct mapping_desc_time * next;
unsigned int timestamp;
unsigned short eport;
short proto;
char desc[];
};
static struct mapping_desc_time * mappings_list = NULL;
/* add an element to the port mappings descriptions & timestamp list */
static void
add_desc_time(unsigned short eport, int proto,
const char * desc, unsigned int timestamp)
{
struct mapping_desc_time * tmp;
size_t l;
if(!desc)
desc = "miniupnpd";
l = strlen(desc) + 1;
tmp = malloc(sizeof(struct mapping_desc_time) + l);
if(tmp) {
/* fill the element and insert it as head of the list */
tmp->next = mappings_list;
tmp->timestamp = timestamp;
tmp->eport = eport;
tmp->proto = (short)proto;
memcpy(tmp->desc, desc, l);
mappings_list = tmp;
}
}
/* remove an element to the port mappings descriptions & timestamp list */
static void
del_desc_time(unsigned short eport, int proto)
{
struct mapping_desc_time * e;
struct mapping_desc_time * * p;
p = &mappings_list;
e = *p;
while(e) {
if(e->eport == eport && e->proto == (short)proto) {
*p = e->next;
free(e);
return;
} else {
p = &e->next;
e = *p;
}
}
}
/* go through the list and find the description and timestamp */
static void
get_desc_time(unsigned short eport, int proto,
char * desc, int desclen,
unsigned int * timestamp)
{
struct mapping_desc_time * e;
for(e = mappings_list; e; e = e->next) {
if(e->eport == eport && e->proto == (short)proto) {
if(desc)
strlcpy(desc, e->desc, desclen);
if(timestamp)
*timestamp = e->timestamp;
return;
}
}
}
/* --- */
int add_redirect_rule2(
const char * ifname,
const char * rhost,
unsigned short eport,
const char * iaddr,
unsigned short iport,
int proto,
const char * desc,
unsigned int timestamp)
{
struct ip_fw rule;
int r;
if (ipfw_validate_protocol(proto) < 0)
return -1;
if (ipfw_validate_ifname(ifname) < 0)
return -1;
memset(&rule, 0, sizeof(struct ip_fw));
rule.version = IP_FW_CURRENT_API_VERSION;
//rule.fw_number = 1000; // rule number
//rule.context = (void *)desc; // The description is kept in a separate list
rule.fw_prot = proto; // protocol
rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check
rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name
rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); // packet direction
rule.fw_flg |= IP_FW_F_FWD; // forward action
#ifdef USE_IFNAME_IN_RULES
if (ifname != NULL) {
strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); // src interface
rule.fw_in_if.fu_via_if.unit = -1;
}
#endif
if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
syslog(LOG_ERR, "inet_aton(): %m");
return -1;
}
memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
rule.fw_dmsk.s_addr = INADDR_BROADCAST; //TODO check this
IP_FW_SETNDSTP(&rule, 1); // number of external ports
rule.fw_uar.fw_pts[0] = eport; // external port
rule.fw_fwd_ip.sin_port = iport; // internal port
if (rhost && rhost[0] != '\0') {
inet_aton(rhost, &rule.fw_src);
rule.fw_smsk.s_addr = htonl(INADDR_NONE);
}
r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
if(r >= 0)
add_desc_time(eport, proto, desc, timestamp);
return r;
}
/* get_redirect_rule()
* return value : 0 success (found)
* -1 = error or rule not found */
int get_redirect_rule(
const char * ifname,
unsigned short eport,
int proto,
char * iaddr,
int iaddrlen,
unsigned short * iport,
char * desc,
int desclen,
char * rhost,
int rhostlen,
unsigned int * timestamp,
u_int64_t * packets,
u_int64_t * bytes)
{
int i, count_rules, total_rules = 0;
struct ip_fw * rules = NULL;
if (ipfw_validate_protocol(proto) < 0)
return -1;
if (ipfw_validate_ifname(ifname) < 0)
return -1;
if (timestamp)
*timestamp = 0;
do {
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
if (count_rules < 0)
goto error;
} while (count_rules == 10);
for (i=0; i<total_rules-1; i++) {
const struct ip_fw const * ptr = &rules[i];
if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
if (packets != NULL)
*packets = ptr->fw_pcnt;
if (bytes != NULL)
*bytes = ptr->fw_bcnt;
if (iport != NULL)
*iport = ptr->fw_fwd_ip.sin_port;
if (iaddr != NULL && iaddrlen > 0) {
/* looks like fw_out_if.fu_via_ip is zero */
//if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
if (rhost != NULL && rhostlen > 0) {
if (ptr->fw_src.s_addr == 0)
rhost[0] = '\0';
else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
// And what if we found more than 1 matching rule?
ipfw_free_ruleset(&rules);
get_desc_time(eport, proto, desc, desclen, timestamp);
return 0;
}
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return -1;
}
int delete_redirect_rule(
const char * ifname,
unsigned short eport,
int proto)
{
int i, count_rules, total_rules = 0;
struct ip_fw * rules = NULL;
if (ipfw_validate_protocol(proto) < 0)
return -1;
if (ipfw_validate_ifname(ifname) < 0)
return -1;
do {
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
if (count_rules < 0)
goto error;
} while (count_rules == 10);
for (i=0; i<total_rules-1; i++) {
const struct ip_fw const * ptr = &rules[i];
if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
goto error;
// And what if we found more than 1 matching rule?
ipfw_free_ruleset(&rules);
del_desc_time(eport, proto);
return 0;
}
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return -1;
}
int add_filter_rule2(
const char * ifname,
const char * rhost,
const char * iaddr,
unsigned short eport,
unsigned short iport,
int proto,
const char * desc)
{
//return -1;
return 0; /* nothing to do, always success */
}
int delete_filter_rule(
const char * ifname,
unsigned short eport,
int proto)
{
//return -1;
return 0; /* nothing to do, always success */
}
int get_redirect_rule_by_index(
int index,
char * ifname,
unsigned short * eport,
char * iaddr,
int iaddrlen,
unsigned short * iport,
int * proto,
char * desc,
int desclen,
char * rhost,
int rhostlen,
unsigned int * timestamp,
u_int64_t * packets,
u_int64_t * bytes)
{
int total_rules = 0;
struct ip_fw * rules = NULL;
if (index < 0) // TODO shouldn't we also validate the maximum?
return -1;
if(timestamp)
*timestamp = 0;
ipfw_fetch_ruleset(&rules, &total_rules, index + 1);
if (total_rules > index) {
const struct ip_fw const * ptr = &rules[index];
if (ptr->fw_prot == 0) // invalid rule
goto error;
if (proto != NULL)
*proto = ptr->fw_prot;
if (eport != NULL)
*eport = ptr->fw_uar.fw_pts[0];
if (iport != NULL)
*iport = ptr->fw_fwd_ip.sin_port;
if (ifname != NULL)
strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ);
if (packets != NULL)
*packets = ptr->fw_pcnt;
if (bytes != NULL)
*bytes = ptr->fw_bcnt;
if (iport != NULL)
*iport = ptr->fw_fwd_ip.sin_port;
if (iaddr != NULL && iaddrlen > 0) {
/* looks like fw_out_if.fu_via_ip is zero */
//if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
if (rhost != NULL && rhostlen > 0) {
if (ptr->fw_src.s_addr == 0)
rhost[0] = '\0';
else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
ipfw_free_ruleset(&rules);
get_desc_time(*eport, *proto, desc, desclen, timestamp);
return 0;
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return -1;
}
/* upnp_get_portmappings_in_range()
* return a list of all "external" ports for which a port
* mapping exists */
unsigned short *
get_portmappings_in_range(unsigned short startport,
unsigned short endport,
int proto,
unsigned int * number)
{
unsigned short * array = NULL;
unsigned int capacity = 128;
int i, count_rules, total_rules = 0;
struct ip_fw * rules = NULL;
if (ipfw_validate_protocol(proto) < 0)
return NULL;
do {
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
if (count_rules < 0)
goto error;
} while (count_rules == 10);
array = calloc(capacity, sizeof(unsigned short));
if(!array) {
syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
goto error;
}
*number = 0;
for (i=0; i<total_rules-1; i++) {
const struct ip_fw const * ptr = &rules[i];
unsigned short eport = ptr->fw_uar.fw_pts[0];
if (proto == ptr->fw_prot
&& startport <= eport
&& eport <= endport) {
if(*number >= capacity) {
capacity += 128;
array = realloc(array, sizeof(unsigned short)*capacity);
if(!array) {
syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
*number = 0;
goto error;
}
}
array[*number] = eport;
(*number)++;
}
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return array;
}

79
miniupnpd/ipfw/ipfwrdr.h Normal file
View File

@ -0,0 +1,79 @@
/* $Id: ipfwrdr.h,v 1.5 2011/06/04 15:47:18 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009 Jardel Weyrich
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#ifndef __IPFWRDR_H__
#define __IPFWRDR_H__
#include "../commonrdr.h"
int add_redirect_rule2(
const char * ifname, // src interface (external)
const char * rhost, // remote host (ip)
unsigned short eport, // src port (external)
const char * iaddr, // dst address (internal)
unsigned short iport, // dst port (internal)
int proto,
const char * desc,
unsigned int timestamp);
int add_filter_rule2(
const char * ifname,
const char * rhost,
const char * iaddr,
unsigned short eport,
unsigned short iport,
int proto,
const char * desc);
#if 0
//
// get_redirect_rule() gets internal IP and port from
// interface, external port and protocl
//
int get_redirect_rule(
const char * ifname,
unsigned short eport,
int proto,
char * iaddr,
int iaddrlen,
unsigned short * iport,
char * desc,
int desclen,
u_int64_t * packets,
u_int64_t * bytes);
int get_redirect_rule_by_index(
int index,
char * ifname,
unsigned short * eport,
char * iaddr,
int iaddrlen,
unsigned short * iport,
int * proto,
char * desc,
int desclen,
u_int64_t * packets,
u_int64_t * bytes);
#endif
//
// delete_redirect_rule()
//
int delete_redirect_rule(const char * ifname, unsigned short eport, int proto);
//
// delete_filter_rule()
//
int delete_filter_rule(const char * ifname, unsigned short eport, int proto);
int clear_redirect_rules(void);
#endif

View File

@ -0,0 +1,87 @@
/* $Id: testipfwrdr.c,v 1.7 2011/06/22 21:57:17 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009-2011 Jardel Weyrich, Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#include <stdio.h>
#include <syslog.h>
#include <time.h>
#include <netinet/in.h>
#include "ipfwrdr.h"
// test program for ipfwrdr.c
static const char * ifname = "lo0";
static void
list_port_mappings(void)
{
int i;
unsigned short eport;
char iaddr[16];
unsigned short iport;
int proto;
char desc[64];
char rhost[32];
unsigned int timestamp;
u_int64_t packets, bytes;
printf("== Port Mapping List ==\n");
for(i = 0;; i++) {
iaddr[0] = '\0';
desc[0] = '\0';
eport = iport = 0;
timestamp = 0;
packets = bytes = 0;
proto = -1;
if(get_redirect_rule_by_index(i, 0/*ifname*/, &eport, iaddr, sizeof(iaddr),
&iport, &proto, desc, sizeof(desc),
rhost, sizeof(rhost),
&timestamp, &packets, &bytes) < 0)
break;
printf("%2d - %5hu=>%15s:%5hu %d '%s' '%s' %u %" PRIu64 " %" PRIu64 "\n",
i, eport, iaddr, iport, proto, desc, rhost, timestamp,
packets, bytes);
}
printf("== %d Port Mapping%s ==\n", i, (i > 1)?"s":"");
}
int main(int argc, char * * argv) {
unsigned int timestamp;
char desc[64];
char addr[16];
char rhost[40];
unsigned short iport = 0;
const char * in_rhost = "8.8.8.8";
desc[0] = '\0';
addr[0] = '\0';
openlog("testipfwrdrd", LOG_CONS | LOG_PERROR, LOG_USER);
if(init_redirect() < 0) {
fprintf(stderr, "init_redirect() failed.\n");
return 1;
}
list_port_mappings();
delete_redirect_rule(ifname, 2222, IPPROTO_TCP);
delete_redirect_rule(ifname, 2223, IPPROTO_TCP);
add_redirect_rule2(ifname, "", 2223,
"10.1.1.17", 4445, IPPROTO_TCP,
"test miniupnpd", time(NULL) + 60);
add_redirect_rule2(ifname, in_rhost, 2222,
"10.1.1.16", 4444, IPPROTO_TCP,
"test miniupnpd", time(NULL) + 60);
get_redirect_rule(ifname, 2222, IPPROTO_TCP, addr, sizeof(addr), &iport,
desc, sizeof(desc), rhost, sizeof(rhost),
&timestamp, NULL, NULL);
printf("'%s' %s:%hu '%s' %u\n", rhost, addr, iport, desc, timestamp);
list_port_mappings();
delete_redirect_rule(ifname, 2222, IPPROTO_TCP);
delete_redirect_rule(ifname, 2223, IPPROTO_TCP);
list_port_mappings();
shutdown_redirect();
return 0;
}

View File

@ -0,0 +1,109 @@
/* $Id: getifstats.c,v 1.9 2011/05/25 22:22:57 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <time.h>
#include "../config.h"
#include "../getifstats.h"
int
getifstats(const char * ifname, struct ifdata * data)
{
FILE *f;
char line[512];
char * p;
int i;
int r = -1;
char fname[64];
#ifdef ENABLE_GETIFSTATS_CACHING
static time_t cache_timestamp = 0;
static struct ifdata cache_data;
time_t current_time;
#endif
if(!data)
return -1;
data->baudrate = 4200000; /* that is the answer */
data->opackets = 0;
data->ipackets = 0;
data->obytes = 0;
data->ibytes = 0;
if(!ifname || ifname[0]=='\0')
return -1;
#ifdef ENABLE_GETIFSTATS_CACHING
current_time = time(NULL);
if(current_time == ((time_t)-1)) {
syslog(LOG_ERR, "getifstats() : time() error : %m");
} else {
if(current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION) {
/* return cached data */
memcpy(data, &cache_data, sizeof(struct ifdata));
return 0;
}
}
#endif
f = fopen("/proc/net/dev", "r");
if(!f) {
syslog(LOG_ERR, "getifstats() : cannot open /proc/net/dev : %m");
return -1;
}
/* discard the two header lines */
if(!fgets(line, sizeof(line), f) || !fgets(line, sizeof(line), f)) {
syslog(LOG_ERR, "getifstats() : error reading /proc/net/dev : %m");
}
while(fgets(line, sizeof(line), f)) {
p = line;
while(*p==' ') p++;
i = 0;
while(ifname[i] == *p) {
p++; i++;
}
/* TODO : how to handle aliases ? */
if(ifname[i] || *p != ':')
continue;
p++;
while(*p==' ') p++;
data->ibytes = strtoul(p, &p, 0);
while(*p==' ') p++;
data->ipackets = strtoul(p, &p, 0);
/* skip 6 columns */
for(i=6; i>0 && *p!='\0'; i--) {
while(*p==' ') p++;
while(*p!=' ' && *p) p++;
}
while(*p==' ') p++;
data->obytes = strtoul(p, &p, 0);
while(*p==' ') p++;
data->opackets = strtoul(p, &p, 0);
r = 0;
break;
}
fclose(f);
/* get interface speed */
snprintf(fname, sizeof(fname), "/sys/class/net/%s/speed", ifname);
f = fopen(fname, "r");
if(f) {
if(fgets(line, sizeof(line), f)) {
data->baudrate = 1000000*atoi(line);
}
fclose(f);
} else {
syslog(LOG_WARNING, "cannot read %s file : %m", fname);
}
#ifdef ENABLE_GETIFSTATS_CACHING
if(r==0 && current_time!=((time_t)-1)) {
/* cache the new data */
cache_timestamp = current_time;
memcpy(&cache_data, data, sizeof(struct ifdata));
}
#endif
return r;
}

View File

@ -0,0 +1,312 @@
/* $Id: ifacewatcher.c,v 1.2 2011/07/30 13:11:39 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2009 Thomas Bernard
*
* ifacewatcher.c
*
* This file implements dynamic serving of new network interfaces
* which weren't available during daemon start. It also takes care
* of interfaces which become unavailable.
*
* Copyright (c) 2011, Alexey Osipov <simba@lerlan.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * 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.
* * The name of the author may not 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 OWNER 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 <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <syslog.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "../config.h"
#ifdef USE_IFACEWATCHER
#include "../ifacewatcher.h"
#include "../minissdp.h"
#include "../getifaddr.h"
#include "../upnpglobalvars.h"
#include "../natpmp.h"
extern volatile int should_send_public_address_change_notif;
int
OpenAndConfInterfaceWatchSocket(void)
{
int s;
struct sockaddr_nl addr;
s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (s == -1)
{
syslog(LOG_ERR, "socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE): %m");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
/*addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;*/
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
syslog(LOG_ERR, "bind(netlink): %m");
close(s);
return -1;
}
return s;
}
#if 0
/* disabled at the moment */
int
ProcessInterfaceUp(struct ifinfomsg *ifi)
{
struct lan_iface_s * lan_iface;
struct lan_iface_s * lan_iface2;
struct lan_addr_s * lan_addr;
char ifname[IFNAMSIZ];
char ifstraddr[16];
struct in_addr ifaddr;
/* check if we already have this iface */
for(lan_iface = lan_ifaces.lh_first; lan_iface != NULL; lan_iface = lan_iface->list.le_next)
if (lan_iface->iface.index == ifi->ifi_index)
break;
if (lan_iface != NULL)
return 0;
if (if_indextoname(ifi->ifi_index, ifname) == NULL)
{
syslog(LOG_ERR, "if_indextoname(%d, ifname) failed", ifi->ifi_index);
return -1;
}
if (getifaddr(ifname, ifstraddr, 16) < 0)
{
syslog(LOG_DEBUG, "getifaddr(%s, ifaddr, 16) failed", ifname);
return 1;
}
if (inet_pton(AF_INET, ifstraddr, &ifaddr) != 1)
{
syslog(LOG_ERR, "inet_pton(AF_INET, \"%s\", &ifaddr) failed", ifstraddr);
return -1;
}
/* check if this new interface has address which we need to listen to */
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
{
if (lan_addr->addr.s_addr != ifaddr.s_addr)
continue;
syslog(LOG_INFO, "Interface up: %s (%s)", ifname, ifstraddr);
/* adding new lan_iface entry */
lan_iface = (struct lan_iface_s *) malloc(sizeof(struct lan_iface_s));
if (lan_iface == NULL)
{
syslog(LOG_ERR, "malloc(sizeof(struct lan_iface_s): %m");
continue;
}
lan_iface->lan_addr = lan_addr;
strncpy(lan_iface->iface.name, ifname, IFNAMSIZ);
lan_iface->iface.index = ifi->ifi_index;
lan_iface->iface.addr = ifaddr;
lan_iface->snotify = -1;
#ifdef ENABLE_NATPMP
lan_iface->snatpmp = -1;
#endif
LIST_INSERT_HEAD(&lan_ifaces, lan_iface, list);
/* adding multicast membership for SSDP */
if(AddMulticastMembership(sudp, ifaddr.s_addr, ifi->ifi_index) < 0)
syslog(LOG_WARNING, "Failed to add multicast membership for interface %s (%s)", ifname, ifstraddr);
else
syslog(LOG_INFO, "Multicast membership added for %s (%s)", ifname, ifstraddr);
/* create SSDP notify socket */
if (OpenAndConfSSDPNotifySocket(lan_iface) < 0)
syslog(LOG_WARNING, "Failed to open SSDP notify socket for interface %s (%s)", ifname, ifstraddr);
#ifdef ENABLE_NATPMP
/* create NAT-PMP socket */
for(lan_iface2 = lan_ifaces.lh_first; lan_iface2 != NULL; lan_iface2 = lan_iface2->list.le_next)
if (lan_iface2->lan_addr->addr.s_addr == lan_iface->lan_addr->addr.s_addr &&
lan_iface2->snatpmp >= 0)
lan_iface->snatpmp = lan_iface2->snatpmp;
if (lan_iface->snatpmp < 0)
{
lan_iface->snatpmp = OpenAndConfNATPMPSocket(ifaddr.s_addr);
if (lan_iface->snatpmp < 0)
syslog(LOG_ERR, "OpenAndConfNATPMPSocket(ifaddr.s_addr) failed for %s (%s)", ifname, ifstraddr);
else
syslog(LOG_INFO, "Listening for NAT-PMP connection on %s:%d", ifstraddr, NATPMP_PORT);
}
#endif
}
return 0;
}
int
ProcessInterfaceDown(struct ifinfomsg *ifi)
{
struct lan_iface_s * lan_iface;
struct lan_iface_s * lan_iface2;
/* check if we have this iface */
for(lan_iface = lan_ifaces.lh_first; lan_iface != NULL; lan_iface = lan_iface->list.le_next)
if (lan_iface->iface.index == ifi->ifi_index)
break;
if (lan_iface == NULL)
return 0;
/* one of our interfaces is going down, clean up */
syslog(LOG_INFO, "Interface down: %s", lan_iface->iface.name);
/* remove multicast membership for SSDP */
if(DropMulticastMembership(sudp, lan_iface->lan_addr->addr.s_addr, lan_iface->iface.index) < 0)
syslog(LOG_WARNING, "Failed to drop multicast membership for interface %s (%s)", lan_iface->iface.name, lan_iface->lan_addr->str);
else
syslog(LOG_INFO, "Multicast membership dropped for %s (%s)", lan_iface->iface.name, lan_iface->lan_addr->str);
/* closing SSDP notify socket */
close(lan_iface->snotify);
#ifdef ENABLE_NATPMP
/* closing NAT-PMP socket if it's not used anymore */
for(lan_iface2 = lan_ifaces.lh_first; lan_iface2 != NULL; lan_iface2 = lan_iface2->list.le_next)
if (lan_iface2 != lan_iface && lan_iface2->snatpmp == lan_iface->snatpmp)
break;
if (lan_iface2 == NULL)
close(lan_iface->snatpmp);
#endif
/* del corresponding lan_iface entry */
LIST_REMOVE(lan_iface, list);
free(lan_iface);
return 0;
}
#endif
void
ProcessInterfaceWatchNotify(int s)
{
char buffer[4096];
struct iovec iov;
struct msghdr hdr;
struct nlmsghdr *nlhdr;
struct ifinfomsg *ifi;
struct ifaddrmsg *ifa;
int len;
struct rtattr *rth; //
int rtl;
unsigned int ext_if_name_index = 0;
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
memset(&hdr, 0, sizeof(hdr));
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
len = recvmsg(s, &hdr, 0);
if (len < 0)
{
syslog(LOG_ERR, "recvmsg(s, &hdr, 0): %m");
return;
}
if(ext_if_name) {
ext_if_name_index = if_nametoindex(ext_if_name);
}
for (nlhdr = (struct nlmsghdr *) buffer;
NLMSG_OK (nlhdr, len);
nlhdr = NLMSG_NEXT (nlhdr, len))
{
if (nlhdr->nlmsg_type == NLMSG_DONE)
break;
switch(nlhdr->nlmsg_type) {
case RTM_DELLINK:
ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
/*if (ProcessInterfaceDown(ifi) < 0)
syslog(LOG_ERR, "ProcessInterfaceDown(ifi) failed");*/
break;
case RTM_NEWLINK:
ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
/*if (ProcessInterfaceUp(ifi) < 0)
syslog(LOG_ERR, "ProcessInterfaceUp(ifi) failed");*/
break;
case RTM_NEWADDR:
/* see /usr/include/linux/netlink.h
* and /usr/include/linux/rtnetlink.h */
ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr);
syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify RTM_NEWADDR");
for(rth = IFA_RTA(ifa), rtl = IFA_PAYLOAD(nlhdr);
rtl && RTA_OK(rth, rtl);
rth = RTA_NEXT(rth, rtl)) {
syslog(LOG_DEBUG, " - %u - %s type=%d",
ifa->ifa_index, inet_ntoa(*((struct in_addr *)RTA_DATA(rth))),
rth->rta_type);
}
if(ifa->ifa_index == ext_if_name_index) {
should_send_public_address_change_notif = 1;
}
break;
case RTM_DELADDR:
ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr);
syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify RTM_DELADDR");
if(ifa->ifa_index == ext_if_name_index) {
should_send_public_address_change_notif = 1;
}
break;
default:
syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify type %d ignored", nlhdr->nlmsg_type);
}
}
}
#endif

View File

@ -0,0 +1,40 @@
#!/bin/sh
# $Id: miniupnpd.init.d.script,v 1.2 2007/09/23 16:11:12 nanard Exp $
# MiniUPnP project
# author: Thomas Bernard
# website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
MINIUPNPD=/usr/sbin/miniupnpd
ARGS='-f /etc/miniupnpd/miniupnpd.conf'
IPTABLES_CREATE=/etc/miniupnpd/iptables_init.sh
IPTABLES_REMOVE=/etc/miniupnpd/iptables_removeall.sh
test -f $MINIUPNPD || exit 0
. /lib/lsb/init-functions
case "$1" in
start) log_daemon_msg "Starting miniupnpd" "miniupnpd"
$IPTABLES_CREATE > /dev/null 2>&1
start-stop-daemon --start --quiet --pidfile /var/run/miniupnpd.pid --startas $MINIUPNPD -- $ARGS $LSBNAMES
log_end_msg $?
;;
stop) log_daemon_msg "Stopping miniupnpd" "miniupnpd"
start-stop-daemon --stop --quiet --pidfile /var/run/miniupnpd.pid
log_end_msg $?
$IPTABLES_REMOVE > /dev/null 2>&1
;;
restart|reload|force-reload)
log_daemon_msg "Restarting miniupnpd" "miniupnpd"
start-stop-daemon --stop --retry 5 --quiet --pidfile /var/run/miniupnpd.pid
$IPTABLES_REMOVE > /dev/null 2>&1
$IPTABLES_CREATE > /dev/null 2>&1
start-stop-daemon --start --quiet --pidfile /var/run/miniupnpd.pid --startas $MINIUPNPD -- $ARGS $LSBNAMES
log_end_msg $?
;;
*) log_action_msg "Usage: /etc/init.d/miniupnpd {start|stop|restart|reload|force-reload}"
exit 2
;;
esac
exit 0

14
miniupnpd/mac/Makefile Normal file
View File

@ -0,0 +1,14 @@
# $Id: Makefile,v 1.2 2011/02/20 23:43:41 nanard Exp $
# made for GNU Make
CFLAGS = -Wall -g
EXECUTABLES = testgetifstats
all: $(EXECUTABLES)
clean:
rm -f *.o $(EXECUTABLES)
testmacrdr.o: testmacrdr.c macrdr.h
testgetifstats: testgetifstats.o getifstats.o

View File

@ -0,0 +1,94 @@
/* $Id: getifstats.c,v 1.5 2011/07/03 22:22:04 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009 Jardel Weyrich
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#include <syslog.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../getifstats.h"
#include "../config.h"
int getifstats(const char * ifname, struct ifdata * data) {
int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(ifname) };
const size_t mib_len = sizeof(mib) / sizeof(mib[0]);
size_t needed;
char *buf, *end, *p;
struct if_msghdr *ifm;
struct if_data ifdata;
#ifdef ENABLE_GETIFSTATS_CACHING
static time_t cache_timestamp = 0;
static struct ifdata cache_data;
time_t current_time;
#endif
if (data == NULL || ifname == NULL || ifname[0] == '\0')
return -1; // error
data->baudrate = 4200000;
data->opackets = 0;
data->ipackets = 0;
data->obytes = 0;
data->ibytes = 0;
#ifdef ENABLE_GETIFSTATS_CACHING
current_time = time(NULL);
if (current_time == ((time_t)-1)) {
syslog(LOG_ERR, "getifstats() : time() error : %m");
} else {
if (current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION) {
memcpy(data, &cache_data, sizeof(struct ifdata));
return 0;
}
}
#endif
if (sysctl(mib, mib_len, NULL, &needed, NULL, 0) == -1) {
syslog(LOG_ERR, "sysctl(): %m");
return -1;
}
buf = (char *) malloc(needed);
if (buf == NULL)
return -1; // error
if (sysctl(mib, mib_len, buf, &needed, NULL, 0) == -1) {
syslog(LOG_ERR, "sysctl(): %m");
free(buf);
return -1; // error
} else {
for (end = buf + needed, p = buf; p < end; p += ifm->ifm_msglen) {
ifm = (struct if_msghdr *) p;
if (ifm->ifm_type == RTM_IFINFO && ifm->ifm_data.ifi_type == IFT_ETHER) {
ifdata = ifm->ifm_data;
data->opackets = ifdata.ifi_opackets;
data->ipackets = ifdata.ifi_ipackets;
data->obytes = ifdata.ifi_obytes;
data->ibytes = ifdata.ifi_ibytes;
data->baudrate = ifdata.ifi_baudrate;
free(buf);
#ifdef ENABLE_GETIFSTATS_CACHING
if (current_time!=((time_t)-1)) {
cache_timestamp = current_time;
memcpy(&cache_data, data, sizeof(struct ifdata));
}
#endif
return 0; // found, ok
}
}
}
free(buf);
return -1; // not found or error
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>miniupnpd</key>
<string>org.tuxfamily.miniupnpd</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/miniupnpd</string>
<string>-f /etc/miniupnpd/miniupnpd.conf</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,29 @@
/* $Id: testgetifstats.c,v 1.3 2011/02/20 23:43:41 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009 Jardel Weyrich
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#include <stdio.h>
#include "../getifstats.h"
int main(int argc, char * * argv) {
int r;
struct ifdata data;
printf("usage: %s if_name\n", argv[0]);
if (argc < 2)
return -1;
r = getifstats(argv[1], &data);
if (r < 0)
printf("getifstats() failed\n");
else {
printf("ipackets = %10lu opackets = %10lu\n", data.ipackets, data.opackets);
printf("ibytes = %10lu obytes = %10lu\n", data.ibytes, data.obytes);
printf("baudrate = %10lu\n", data.baudrate);
}
return 0;
}

635
miniupnpd/minissdp.c Normal file
View File

@ -0,0 +1,635 @@
/* $Id: minissdp.c,v 1.27 2011/05/23 12:39:41 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include "config.h"
#include "upnpdescstrings.h"
#include "miniupnpdpath.h"
#include "upnphttp.h"
#include "upnpglobalvars.h"
#include "minissdp.h"
#include "upnputils.h"
#include "codelength.h"
/* SSDP ip/port */
#define SSDP_PORT (1900)
#define SSDP_MCAST_ADDR ("239.255.255.250")
#define LL_SSDP_MCAST_ADDR ("FF02::C")
#define SL_SSDP_MCAST_ADDR ("FF05::C")
/* AddMulticastMembership()
* param s socket
* param ifaddr ip v4 address
*/
static int
AddMulticastMembership(int s, in_addr_t ifaddr)
{
struct ip_mreq imr; /* Ip multicast membership */
/* setting up imr structure */
imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
/*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
return -1;
}
return 0;
}
/* AddMulticastMembershipIPv6()
* param s socket (IPv6)
* To be improved to target specific network interfaces */
#ifdef ENABLE_IPV6
static int
AddMulticastMembershipIPv6(int s)
{
struct ipv6_mreq mr;
/*unsigned int ifindex;*/
memset(&mr, 0, sizeof(mr));
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
/*mr.ipv6mr_interface = ifindex;*/
mr.ipv6mr_interface = 0; /* 0 : all interfaces */
#ifndef IPV6_ADD_MEMBERSHIP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
return -1;
}
inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
return -1;
}
return 0;
}
#endif
/* Open and configure the socket listening for
* SSDP udp packets sent on 239.255.255.250 port 1900
* SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
int
OpenAndConfSSDPReceiveSocket(int ipv6)
{
int s;
struct sockaddr_storage sockname;
socklen_t sockname_len;
struct lan_addr_s * lan_addr;
int j = 1;
if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
{
syslog(LOG_ERR, "socket(udp): %m");
return -1;
}
memset(&sockname, 0, sizeof(struct sockaddr_storage));
if(ipv6) {
struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
saddr->sin6_family = AF_INET6;
saddr->sin6_port = htons(SSDP_PORT);
saddr->sin6_addr = in6addr_any;
sockname_len = sizeof(struct sockaddr_in6);
} else {
struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
saddr->sin_family = AF_INET;
saddr->sin_port = htons(SSDP_PORT);
/* NOTE : it seems it doesnt work when binding on the specific address */
/*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
saddr->sin_addr.s_addr = htonl(INADDR_ANY);
/*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
sockname_len = sizeof(struct sockaddr_in);
}
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
{
syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
}
if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
{
syslog(LOG_ERR, "bind(udp%s): %m", ipv6 ? "6" : "");
close(s);
return -1;
}
#ifdef ENABLE_IPV6
if(ipv6)
{
AddMulticastMembershipIPv6(s);
}
else
#endif
{
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
{
if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
{
syslog(LOG_WARNING,
"Failed to add multicast membership for interface %s",
lan_addr->str);
}
}
}
return s;
}
/* open the UDP socket used to send SSDP notifications to
* the multicast group reserved for them */
static int
OpenAndConfSSDPNotifySocket(in_addr_t addr)
{
int s;
unsigned char loopchar = 0;
int bcast = 1;
struct in_addr mc_if;
struct sockaddr_in sockname;
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
syslog(LOG_ERR, "socket(udp_notify): %m");
return -1;
}
mc_if.s_addr = addr; /*inet_addr(addr);*/
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
close(s);
return -1;
}
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
close(s);
return -1;
}
if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
close(s);
return -1;
}
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
{
syslog(LOG_ERR, "bind(udp_notify): %m");
close(s);
return -1;
}
return s;
}
int
OpenAndConfSSDPNotifySockets(int * sockets)
/*OpenAndConfSSDPNotifySockets(int * sockets,
struct lan_addr_s * lan_addr, int n_lan_addr)*/
{
int i, j;
struct lan_addr_s * lan_addr;
for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++)
{
sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
if(sockets[i] < 0)
{
for(j=0; j<i; j++)
{
close(sockets[j]);
sockets[j] = -1;
}
return -1;
}
}
return 0;
}
/*
* response from a LiveBox (Wanadoo)
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
DATE: Thu, 01 Jan 1970 04:03:23 GMT
EXT:
LOCATION: http://192.168.0.1:49152/gatedesc.xml
SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
ST: upnp:rootdevice
USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
* response from a Linksys 802.11b :
HTTP/1.1 200 OK
Cache-Control:max-age=120
Location:http://192.168.5.1:5678/rootDesc.xml
Server:NT/5.0 UPnP/1.0
ST:upnp:rootdevice
USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
EXT:
*/
/* not really an SSDP "announce" as it is the response
* to a SSDP "M-SEARCH" */
static void
SendSSDPAnnounce2(int s, const struct sockaddr * addr,
const char * st, int st_len, const char * suffix,
const char * host, unsigned short port)
{
int l, n;
char buf[512];
socklen_t addrlen;
/*
* follow guideline from document "UPnP Device Architecture 1.0"
* uppercase is recommended.
* DATE: is recommended
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
* - check what to put in the 'Cache-Control' header
*
* have a look at the document "UPnP Device Architecture v1.1 */
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=120\r\n"
/*"DATE: ...\r\n"*/
"ST: %.*s%s\r\n"
"USN: %s::%.*s%s\r\n"
"EXT:\r\n"
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
"01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"\r\n",
st_len, st, suffix,
uuidvalue, st_len, st, suffix,
host, (unsigned int)port,
upnp_bootid, upnp_bootid, upnp_configid);
addrlen = (addr->sa_family == AF_INET6)
? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
n = sendto(s, buf, l, 0,
addr, addrlen);
syslog(LOG_INFO, "SSDP Announce %d bytes to %s:%d ST: %.*s",n,
inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr),
ntohs(((const struct sockaddr_in *)addr)->sin_port),
l, buf);
if(n < 0)
{
syslog(LOG_ERR, "sendto(udp): %m");
}
}
static const char * const known_service_types[] =
{
"upnp:rootdevice",
"urn:schemas-upnp-org:device:InternetGatewayDevice:",
"urn:schemas-upnp-org:device:WANConnectionDevice:",
"urn:schemas-upnp-org:device:WANDevice:",
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:",
"urn:schemas-upnp-org:service:WANIPConnection:",
"urn:schemas-upnp-org:service:WANPPPConnection:",
#ifdef ENABLE_L3F_SERVICE
"urn:schemas-upnp-org:service:Layer3Forwarding:",
#endif
#ifdef ENABLE_6FC_SERVICE
"url:schemas-upnp-org:service:WANIPv6FirewallControl:",
#endif
0
};
static void
SendSSDPNotifies(int s, const char * host, unsigned short port,
unsigned int lifetime)
{
struct sockaddr_in sockname;
int l, n, i=0;
char bufr[512];
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_port = htons(SSDP_PORT);
sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
while(known_service_types[i])
{
l = snprintf(bufr, sizeof(bufr),
"NOTIFY * HTTP/1.1\r\n"
"HOST: %s:%d\r\n"
"CACHE-CONTROL: max-age=%u\r\n"
"lOCATION: http://%s:%d" ROOTDESC_PATH"\r\n"
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
"NT: %s%s\r\n"
"USN: %s::%s%s\r\n"
"NTS: ssdp:alive\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
"01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"\r\n",
SSDP_MCAST_ADDR, SSDP_PORT,
lifetime,
host, port,
known_service_types[i], (i==0?"":"1"),
uuidvalue, known_service_types[i], (i==0?"":"1"),
upnp_bootid, upnp_bootid, upnp_configid );
if(l>=sizeof(bufr))
{
syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
l = sizeof(bufr);
}
n = sendto(s, bufr, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0)
{
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
}
i++;
}
}
void
SendSSDPNotifies2(int * sockets,
unsigned short port,
unsigned int lifetime)
{
int i;
struct lan_addr_s * lan_addr;
for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++)
{
SendSSDPNotifies(sockets[i], lan_addr->str, port, lifetime);
}
}
/* ProcessSSDPRequest()
* process SSDP M-SEARCH requests and responds to them */
void
ProcessSSDPRequest(int s, unsigned short port)
{
int n;
char bufr[1500];
socklen_t len_r;
#ifdef ENABLE_IPV6
struct sockaddr_storage sendername;
len_r = sizeof(struct sockaddr_storage);
#else
struct sockaddr_in sendername;
len_r = sizeof(struct sockaddr_in);
#endif
n = recvfrom(s, bufr, sizeof(bufr), 0,
(struct sockaddr *)&sendername, &len_r);
if(n < 0)
{
syslog(LOG_ERR, "recvfrom(udp): %m");
return;
}
ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
}
void
ProcessSSDPData(int s, const char *bufr, int n,
const struct sockaddr * sender, unsigned short port) {
int i, l;
struct lan_addr_s * lan_addr = NULL;
const char * st = NULL;
int st_len = 0;
char sender_str[64];
const char * announced_host = NULL;
/* get the string representation of the sender address */
sockaddr_to_string(sender, sender_str, sizeof(sender_str));
if(memcmp(bufr, "NOTIFY", 6) == 0)
{
/* ignore NOTIFY packets. We could log the sender and device type */
return;
}
else if(memcmp(bufr, "M-SEARCH", 8) == 0)
{
i = 0;
while(i < n)
{
while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
i++;
i += 2;
if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
{
st = bufr+i+3;
st_len = 0;
while((*st == ' ' || *st == '\t') && (st < bufr + n))
st++;
while(st[st_len]!='\r' && st[st_len]!='\n'
&& (st + st_len < bufr + n))
st_len++;
/*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
/*j = 0;*/
/*while(bufr[i+j]!='\r') j++;*/
/*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
}
}
/*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
sender_str );*/
if(st && (st_len > 0))
{
/* TODO : doesnt answer at once but wait for a random time */
syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
sender_str, st_len, st);
/* find in which sub network the client is */
if(sender->sa_family == AF_INET)
{
for(lan_addr = lan_addrs.lh_first;
lan_addr != NULL;
lan_addr = lan_addr->list.le_next)
{
if( (((const struct sockaddr_in *)sender)->sin_addr.s_addr & lan_addr->mask.s_addr)
== (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
break;
}
if (lan_addr == NULL)
{
syslog(LOG_ERR, "Can't find in which sub network the client is");
return;
}
announced_host = lan_addr->str;
}
#ifdef ENABLE_IPV6
else
{
/* IPv6 address with brackets */
announced_host = ipv6_addr_for_http_with_brackets;
}
#endif
/* Responds to request with a device as ST header */
for(i = 0; known_service_types[i]; i++)
{
l = (int)strlen(known_service_types[i]);
if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
{
syslog(LOG_INFO, "Single search found");
SendSSDPAnnounce2(s, sender,
st, st_len, "",
announced_host, port);
break;
}
}
/* Responds to request with ST: ssdp:all */
/* strlen("ssdp:all") == 8 */
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
{
syslog(LOG_INFO, "ssdp:all found");
for(i=0; known_service_types[i]; i++)
{
l = (int)strlen(known_service_types[i]);
SendSSDPAnnounce2(s, sender,
known_service_types[i], l, i==0?"":"1",
announced_host, port);
}
}
/* responds to request by UUID value */
l = (int)strlen(uuidvalue);
if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
{
syslog(LOG_INFO, "ssdp:uuid found");
SendSSDPAnnounce2(s, sender, st, st_len, "",
announced_host, port);
}
}
else
{
syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
}
}
else
{
syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
}
}
/* This will broadcast ssdp:byebye notifications to inform
* the network that UPnP is going down. */
int
SendSSDPGoodbye(int * sockets, int n_sockets)
{
struct sockaddr_in sockname;
int n, l;
int i, j;
char bufr[512];
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_port = htons(SSDP_PORT);
sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
for(j=0; j<n_sockets; j++)
{
for(i=0; known_service_types[i]; i++)
{
l = snprintf(bufr, sizeof(bufr),
"NOTIFY * HTTP/1.1\r\n"
"HOST: %s:%d\r\n"
"NT: %s%s\r\n"
"USN: %s::%s%s\r\n"
"NTS: ssdp:byebye\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
"01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"\r\n",
SSDP_MCAST_ADDR, SSDP_PORT,
known_service_types[i], (i==0?"":"1"),
uuidvalue, known_service_types[i], (i==0?"":"1"),
upnp_bootid, upnp_bootid, upnp_configid);
n = sendto(sockets[j], bufr, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0)
{
syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
sockets[j]);
return -1;
}
}
}
return 0;
}
/* SubmitServicesToMiniSSDPD() :
* register services offered by MiniUPnPd to a running instance of
* MiniSSDPd */
int
SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
struct sockaddr_un addr;
int s;
unsigned char buffer[2048];
char strbuf[256];
unsigned char * p;
int i, l;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s < 0) {
syslog(LOG_ERR, "socket(unix): %m");
return -1;
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
return -1;
}
for(i = 0; known_service_types[i]; i++) {
buffer[0] = 4;
p = buffer + 1;
l = (int)strlen(known_service_types[i]);
if(i > 0)
l++;
CODELENGTH(l, p);
memcpy(p, known_service_types[i], l);
if(i > 0)
p[l-1] = '1';
p += l;
l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
uuidvalue, known_service_types[i], (i==0)?"":"1");
CODELENGTH(l, p);
memcpy(p, strbuf, l);
p += l;
l = (int)strlen(MINIUPNPD_SERVER_STRING);
CODELENGTH(l, p);
memcpy(p, MINIUPNPD_SERVER_STRING, l);
p += l;
l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
host, (unsigned int)port);
CODELENGTH(l, p);
memcpy(p, strbuf, l);
p += l;
if(write(s, buffer, p - buffer) < 0) {
syslog(LOG_ERR, "write(): %m");
return -1;
}
}
close(s);
return 0;
}

47
miniupnpd/minissdp.h Normal file
View File

@ -0,0 +1,47 @@
/* $Id: minissdp.h,v 1.10 2011/05/23 12:39:41 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2007 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __MINISSDP_H__
#define __MINISSDP_H__
#include "miniupnpdtypes.h"
int
OpenAndConfSSDPReceiveSocket(int ipv6);
int
OpenAndConfSSDPNotifySockets(int * sockets);
/*OpenAndConfSSDPNotifySockets(int * sockets,
struct lan_addr_s * lan_addr, int n_lan_addr);*/
/*void
SendSSDPNotifies(int s, const char * host, unsigned short port,
unsigned int lifetime);*/
void
SendSSDPNotifies2(int * sockets,
unsigned short port,
unsigned int lifetime);
/*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
unsigned short port,
unsigned int lifetime);*/
void
ProcessSSDPRequest(int s, unsigned short port);
/*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
unsigned short port);*/
void
ProcessSSDPData(int s, const char *bufr, int n,
const struct sockaddr * sendername, unsigned short port);
int
SendSSDPGoodbye(int * sockets, int n);
int
SubmitServicesToMiniSSDPD(const char * host, unsigned short port);
#endif

73
miniupnpd/miniupnpd.1 Normal file
View File

@ -0,0 +1,73 @@
.TH miniupnpd 1
.SH NAME
miniupnpd \- UPnP Internet Gateway Device Daemon
.SH SYNOPSIS
.B miniupnpd
[-f file] [-i interface] [-o address]
[-a address] [-p port] [-d] [-L] [-U]
[-u uuid] [-s serial] [-m model_number]
[-q queue]
[-t interval] [-P file]
[-B down up] [-w url]
.SH DESCRIPTION
miniupnpd act as a UPnP Internet Gateway Device. It is designed
to run on the gateway between the internet and a NAT'ed LAN. It provides
an interface, as defined in the UPnP standard, for enabling
clients on the LAN to ask for port redirections.
.SH OPTIONS
.TP
.B \-f file
load the config from file. default is /etc/miniupnpd.conf.
.TP
.B \-i interface
interface used to connect to the internet.
.TP
.B \-o address
address used to connect to the internet.
default address of the interface will be used if not specified.
.TP
.B \-a address
address on the LAN. -a option can by used multiple time if LAN is
subdivised in several subnetworks.
.TP
.B \-p port
port used for HTTP.
.TP
.B \-d
debug mode : do not go to background, output messages on console
and do not filter out low priority messages.
.TP
.B \-L
set packet log in pf on
.TP
.B \-q queue
set ALTQ queue in pf. filter rules must be enabled for this option
to have any effect.
.TP
.B \-U
report system uptime instead of daemon uptime to clients.
.TP
.B \-u uuid
set the uuid of the UPnP Internet Gateway Device.
.TP
.B \-s serial
serial number for the UPnP Internet Gateway Device.
.TP
.B \-m number
model number for the UPnP Internet Gateway Device.
.TP
.B \-t interval
SSDP notify interval in seconds :
SSDP announce messages will be broadcasted at this interval.
.TP
.B \-P file
pid file. default is /var/run/miniupnpd.pid
.TP
.B \-B down up
download and upload bitrates reported to clients.
.TP
.B \-w url
presentation url. default is first address on LAN, port 80.
.SH "SEE ALSO"
minissdpd(1) miniupnpc(3)
.SH BUGS

1697
miniupnpd/miniupnpd.c Normal file

File diff suppressed because it is too large Load Diff

105
miniupnpd/miniupnpd.conf Normal file
View File

@ -0,0 +1,105 @@
# WAN network interface
ext_ifname=eth1
#ext_ifname=xl1
# if the WAN interface has several IP addresses, you
# can specify the one to use below
#ext_ip=
# LAN network interfaces IPs / networks
# there can be multiple listening ips for SSDP traffic.
# should be under the form nnn.nnn.nnn.nnn/nn
# HTTP is available on all interfaces
# When MULTIPLE_EXTERNAL_IP is enabled, the external ip
# address associated with the subnet follows. for example :
# listening_ip=192.168.0.1/24 88.22.44.13
#listening_ip=192.168.0.1/24
listening_ip=192.168.11.33/24
#listening_ip=
# port for HTTP (descriptions and SOAP) traffic. set 0 for autoselect.
port=0
# path to the unix socket used to communicate with MiniSSDPd
# If running, MiniSSDPd will manage M-SEARCH answering.
# default is /var/run/minissdpd.sock
#minissdpdsocket=/var/run/minissdpd.sock
# enable NAT-PMP support (default is no)
enable_natpmp=yes
# enable UPNP support (default is yes)
enable_upnp=yes
# chain names for netfilter (not used for pf or ipf).
# default is MINIUPNPD for both
#upnp_forward_chain=forwardUPnP
#upnp_nat_chain=UPnP
# lease file location
#lease_file=/var/log/upnp.leases
# bitrates reported by daemon in bits per second
bitrate_up=1000000
bitrate_down=10000000
# "secure" mode : when enabled, UPnP client are allowed to add mappings only
# to their IP.
#secure_mode=yes
secure_mode=no
# default presentation url is http address on port 80
# If set to an empty string, no presentationURL element will appear
# in the XML description of the device, which prevents MS Windows
# from displaying an icon in the "Network Connections" panel.
#presentation_url=http://www.mylan/index.php
# report system uptime instead of daemon uptime
system_uptime=yes
# notify interval in seconds. default is 30 seconds.
#notify_interval=240
notify_interval=60
# unused rules cleaning.
# never remove any rule before this threshold for the number
# of redirections is exceeded. default to 20
#clean_ruleset_threshold=10
# clean process work interval in seconds. default to 0 (disabled).
# a 600 seconds (10 minutes) interval makes sense
clean_ruleset_interval=600
# log packets in pf
#packet_log=no
# ALTQ queue in pf
# filter rules must be used for this to be used.
# compile with PF_ENABLE_FILTER_RULES (see config.h file)
#queue=queue_name1
# tag name in pf
#tag=tag_name1
# make filter rules in pf quick or not. default is yes
# active when compiled with PF_ENABLE_FILTER_RULES (see config.h file)
#quickrules=no
# uuid : generate your own with "make genuuid"
uuid=3d3cec3a-8cf0-11e0-98ee-001a6bd2d07b
# serial and model number the daemon will report to clients
# in its XML description
serial=12345678
model_number=1
# UPnP permission rules
# (allow|deny) (external port range) ip/mask (internal port range)
# A port range is <min port>-<max port> or <port> if there is only
# one port in the range.
# ip/mask format must be nn.nn.nn.nn/nn
# it is advised to only allow redirection of port above 1024
# and to finish the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
allow 1024-65535 192.168.0.0/24 1024-65535
allow 1024-65535 192.168.1.0/24 1024-65535
allow 1024-65535 192.168.0.0/23 22
allow 12345 192.168.7.113/32 54321
deny 0-65535 0.0.0.0/0 0-65535

79
miniupnpd/miniupnpdctl.c Normal file
View File

@ -0,0 +1,79 @@
/* $Id: miniupnpdctl.c,v 1.8 2010/02/15 10:19:46 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#if 0
static void sighandler(int sig)
{
printf("received signal %d\n", sig);
}
#endif
int
main(int argc, char * * argv)
{
/*char test[] = "test!";*/
static const char command[] = "show all\n";
char buf[256];
int l;
int s;
struct sockaddr_un addr;
// signal(SIGINT, sighandler);
s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s<0)
{
perror("socket");
return 1;
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "/var/run/miniupnpd.ctl",
sizeof(addr.sun_path));
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
{
perror("connect");
close(s);
return 1;
}
printf("Connected.\n");
if(write(s, command, sizeof(command)) < 0)
{
perror("write");
close(s);
return 1;
}
for(;;)
{
l = read(s, buf, sizeof(buf));
if(l<0)
{
perror("read");
break;
}
if(l==0)
break;
/*printf("%d bytes read\n", l);*/
fflush(stdout);
if(write(fileno(stdout), buf, l) < 0) {
perror("error writing to stdout");
}
/*printf("\n");*/
}
close(s);
return 0;
}

49
miniupnpd/miniupnpdpath.h Normal file
View File

@ -0,0 +1,49 @@
/* $Id: miniupnpdpath.h,v 1.8 2011/05/20 17:51:23 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __MINIUPNPDPATH_H__
#define __MINIUPNPDPATH_H__
#include "config.h"
/* Paths and other URLs in the miniupnpd http server */
#define ROOTDESC_PATH "/rootDesc.xml"
#ifdef HAS_DUMMY_SERVICE
#define DUMMY_PATH "/dummy.xml"
#endif
#define WANCFG_PATH "/WANCfg.xml"
#define WANCFG_CONTROLURL "/ctl/CmnIfCfg"
#define WANCFG_EVENTURL "/evt/CmnIfCfg"
#define WANIPC_PATH "/WANIPCn.xml"
#define WANIPC_CONTROLURL "/ctl/IPConn"
#define WANIPC_EVENTURL "/evt/IPConn"
#ifdef ENABLE_L3F_SERVICE
#define L3F_PATH "/L3F.xml"
#define L3F_CONTROLURL "/ctl/L3F"
#define L3F_EVENTURL "/evt/L3F"
#endif
#ifdef ENABLE_6FC_SERVICE
#define WANIP6FC_PATH "/WANIP6FC.xml"
#define WANIP6FC_CONTROLURL "/ctl/IP6FCtl"
#define WANIP6FC_EVENTURL "/evt/IP6FCtl"
#endif
#ifdef ENABLE_DP_SERVICE
/* For DeviceProtection introduced in IGD v2 */
#define DP_PATH "/DP.xml"
#define DP_CONTROLURL "/ctl/DP"
#define DP_EVENTURL "/evt/DP"
#endif
#endif

View File

@ -0,0 +1,27 @@
/* $Id: miniupnpdtypes.h,v 1.3 2011/05/13 13:56:18 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2007 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __MINIUPNPDTYPES_H__
#define __MINIUPNPDTYPES_H__
#include "config.h"
#include <netinet/in.h>
#include <sys/queue.h>
/* structure and list for storing lan addresses
* with ascii representation and mask */
struct lan_addr_s {
char str[16]; /* example: 192.168.0.1 */
struct in_addr addr, mask; /* ip/mask */
#ifdef MULTIPLE_EXTERNAL_IP
char ext_ip_str[16];
struct in_addr ext_ip_addr;
#endif
LIST_ENTRY(lan_addr_s) list;
};
LIST_HEAD(lan_addr_list, lan_addr_s);
#endif

216
miniupnpd/minixml.c Normal file
View File

@ -0,0 +1,216 @@
/* $Id: minixml.c,v 1.9 2011/02/07 13:44:57 nanard Exp $ */
/* minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
Copyright (c) 2005-2011, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* 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.
* The name of the author may not 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 OWNER 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 <string.h>
#include "minixml.h"
/* parseatt : used to parse the argument list
* return 0 (false) in case of success and -1 (true) if the end
* of the xmlbuffer is reached. */
static int parseatt(struct xmlparser * p)
{
const char * attname;
int attnamelen;
const char * attvalue;
int attvaluelen;
while(p->xml < p->xmlend)
{
if(*p->xml=='/' || *p->xml=='>')
return 0;
if( !IS_WHITE_SPACE(*p->xml) )
{
char sep;
attname = p->xml;
attnamelen = 0;
while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
{
attnamelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
while(*(p->xml++) != '=')
{
if(p->xml >= p->xmlend)
return -1;
}
while(IS_WHITE_SPACE(*p->xml))
{
p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
sep = *p->xml;
if(sep=='\'' || sep=='\"')
{
p->xml++;
if(p->xml >= p->xmlend)
return -1;
attvalue = p->xml;
attvaluelen = 0;
while(*p->xml != sep)
{
attvaluelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
}
else
{
attvalue = p->xml;
attvaluelen = 0;
while( !IS_WHITE_SPACE(*p->xml)
&& *p->xml != '>' && *p->xml != '/')
{
attvaluelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
}
/*printf("%.*s='%.*s'\n",
attnamelen, attname, attvaluelen, attvalue);*/
if(p->attfunc)
p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
}
p->xml++;
}
return -1;
}
/* parseelt parse the xml stream and
* call the callback functions when needed... */
static void parseelt(struct xmlparser * p)
{
int i;
const char * elementname;
while(p->xml < (p->xmlend - 1))
{
if((p->xml)[0]=='<' && (p->xml)[1]!='?')
{
i = 0; elementname = ++p->xml;
while( !IS_WHITE_SPACE(*p->xml)
&& (*p->xml!='>') && (*p->xml!='/')
)
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
/* to ignore namespace : */
if(*p->xml==':')
{
i = 0;
elementname = ++p->xml;
}
}
if(i>0)
{
if(p->starteltfunc)
p->starteltfunc(p->data, elementname, i);
if(parseatt(p))
return;
if(*p->xml!='/')
{
const char * data;
i = 0; data = ++p->xml;
if (p->xml >= p->xmlend)
return;
while( IS_WHITE_SPACE(*p->xml) )
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
if(memcmp(p->xml, "<![CDATA[", 9) == 0)
{
/* CDATA handling */
p->xml += 9;
data = p->xml;
i = 0;
while(memcmp(p->xml, "]]>", 3) != 0)
{
i++; p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
if(i>0 && p->datafunc)
p->datafunc(p->data, data, i);
while(*p->xml!='<')
{
p->xml++;
if (p->xml >= p->xmlend)
return;
}
}
else
{
while(*p->xml!='<')
{
i++; p->xml++;
if ((p->xml + 1) >= p->xmlend)
return;
}
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
p->datafunc(p->data, data, i);
}
}
}
else if(*p->xml == '/')
{
i = 0; elementname = ++p->xml;
if (p->xml >= p->xmlend)
return;
while((*p->xml != '>'))
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
if(p->endeltfunc)
p->endeltfunc(p->data, elementname, i);
p->xml++;
}
}
else
{
p->xml++;
}
}
}
/* the parser must be initialized before calling this function */
void parsexml(struct xmlparser * parser)
{
parser->xml = parser->xmlstart;
parser->xmlend = parser->xmlstart + parser->xmlsize;
parseelt(parser);
}

37
miniupnpd/minixml.h Normal file
View File

@ -0,0 +1,37 @@
/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
/* minimal xml parser
*
* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef __MINIXML_H__
#define __MINIXML_H__
#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
/* if a callback function pointer is set to NULL,
* the function is not called */
struct xmlparser {
const char *xmlstart;
const char *xmlend;
const char *xml; /* pointer to current character */
int xmlsize;
void * data;
void (*starteltfunc) (void *, const char *, int);
void (*endeltfunc) (void *, const char *, int);
void (*datafunc) (void *, const char *, int);
void (*attfunc) (void *, const char *, int, const char *, int);
};
/* parsexml()
* the xmlparser structure must be initialized before the call
* the following structure members have to be initialized :
* xmlstart, xmlsize, data, *func
* xml is for internal usage, xmlend is computed automatically */
void parsexml(struct xmlparser *);
#endif

422
miniupnpd/natpmp.c Normal file
View File

@ -0,0 +1,422 @@
/* $Id: natpmp.c,v 1.26 2011/07/15 07:48:26 nanard Exp $ */
/* MiniUPnP project
* (c) 2007-2010 Thomas Bernard
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "config.h"
#include "natpmp.h"
#include "upnpglobalvars.h"
#include "getifaddr.h"
#include "upnpredirect.h"
#include "commonrdr.h"
#ifdef ENABLE_NATPMP
int OpenAndConfNATPMPSocket(in_addr_t addr)
{
int snatpmp;
snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);
if(snatpmp<0)
{
syslog(LOG_ERR, "socket(natpmp): %m");
return -1;
}
else
{
struct sockaddr_in natpmp_addr;
memset(&natpmp_addr, 0, sizeof(natpmp_addr));
natpmp_addr.sin_family = AF_INET;
natpmp_addr.sin_port = htons(NATPMP_PORT);
//natpmp_addr.sin_addr.s_addr = INADDR_ANY;
natpmp_addr.sin_addr.s_addr = addr;
if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0)
{
syslog(LOG_ERR, "bind(natpmp): %m");
close(snatpmp);
return -1;
}
}
return snatpmp;
}
int OpenAndConfNATPMPSockets(int * sockets)
{
int i, j;
struct lan_addr_s * lan_addr;
for(i = 0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++)
{
sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr);
if(sockets[i] < 0)
{
for(j=0; j<i; j++)
{
close(sockets[j]);
sockets[j] = -1;
}
return -1;
}
}
return 0;
}
static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr)
{
#ifndef MULTIPLE_EXTERNAL_IP
char tmp[16];
if(use_ext_ip_addr) {
inet_pton(AF_INET, use_ext_ip_addr, resp+8);
} else {
if(!ext_if_name || ext_if_name[0]=='\0') {
resp[3] = 3; /* Network Failure (e.g. NAT box itself
* has not obtained a DHCP lease) */
} else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN) < 0) {
syslog(LOG_ERR, "Failed to get IP for interface %s", ext_if_name);
resp[3] = 3; /* Network Failure (e.g. NAT box itself
* has not obtained a DHCP lease) */
} else {
inet_pton(AF_INET, tmp, resp+8); /* ok */
}
}
#else
struct lan_addr_s * lan_addr;
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
if( (senderaddr & lan_addr->mask.s_addr)
== (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) {
memcpy(resp+8, &lan_addr->ext_ip_addr,
sizeof(lan_addr->ext_ip_addr));
break;
}
}
#endif
}
/** read the request from the socket, process it and then send the
* response back.
*/
void ProcessIncomingNATPMPPacket(int s)
{
unsigned char req[32]; /* request udp packet */
unsigned char resp[32]; /* response udp packet */
int resplen;
struct sockaddr_in senderaddr;
socklen_t senderaddrlen = sizeof(senderaddr);
int n;
char senderaddrstr[16];
n = recvfrom(s, req, sizeof(req), 0,
(struct sockaddr *)&senderaddr, &senderaddrlen);
if(n<0) {
syslog(LOG_ERR, "recvfrom(natpmp): %m");
return;
}
if(!inet_ntop(AF_INET, &senderaddr.sin_addr,
senderaddrstr, sizeof(senderaddrstr))) {
syslog(LOG_ERR, "inet_ntop(natpmp): %m");
}
syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
senderaddrstr, ntohs(senderaddr.sin_port), n);
if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
n);
return;
}
if(req[1] & 128) {
/* discarding NAT-PMP responses silently */
return;
}
memset(resp, 0, sizeof(resp));
resplen = 8;
resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */
/* setting response TIME STAMP :
* time elapsed since its port mapping table was initialized on
* startup or reset for any other reason */
*((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time);
if(req[0] > 0) {
/* invalid version */
syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
(unsigned)req[0]);
resp[3] = 1; /* unsupported version */
} else switch(req[1]) {
case 0: /* Public address request */
syslog(LOG_INFO, "NAT-PMP public address request");
FillPublicAddressResponse(resp, senderaddr.sin_addr.s_addr);
resplen = 12;
break;
case 1: /* UDP port mapping request */
case 2: /* TCP port mapping request */
{
unsigned short iport; /* private port */
unsigned short eport; /* public port */
uint32_t lifetime; /* lifetime=0 => remove port mapping */
int r;
int proto;
char iaddr_old[16];
unsigned short iport_old;
unsigned int timestamp;
iport = ntohs(*((uint16_t *)(req+4)));
eport = ntohs(*((uint16_t *)(req+6)));
lifetime = ntohl(*((uint32_t *)(req+8)));
proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
syslog(LOG_INFO, "NAT-PMP port mapping request : "
"%hu->%s:%hu %s lifetime=%us",
eport, senderaddrstr, iport,
(req[1]==1)?"udp":"tcp", lifetime);
if(eport==0)
eport = iport;
/* TODO: accept port mapping if iport ok but eport not ok
* (and set eport correctly) */
if(lifetime == 0) {
/* remove the mapping */
if(iport == 0) {
/* remove all the mappings for this client */
int index = 0;
unsigned short eport2, iport2;
char iaddr2[16];
int proto2;
char desc[64];
while(get_redirect_rule_by_index(index, 0,
&eport2, iaddr2, sizeof(iaddr2),
&iport2, &proto2,
desc, sizeof(desc),
0, 0, &timestamp, 0, 0) >= 0) {
syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
index, proto2, eport2, iaddr2, iport2, desc);
if(0 == strcmp(iaddr2, senderaddrstr)
&& 0 == memcmp(desc, "NAT-PMP", 7)) {
r = _upnp_delete_redir(eport2, proto2);
/* TODO : check return value */
if(r<0) {
syslog(LOG_ERR, "failed to remove port mapping");
index++;
} else {
syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
}
} else {
index++;
}
}
} else {
/* To improve the interworking between nat-pmp and
* UPnP, we should check that we remove only NAT-PMP
* mappings */
r = _upnp_delete_redir(eport, proto);
/*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
if(r<0) {
syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
resp[3] = 2; /* Not Authorized/Refused */
}
}
eport = 0; /* to indicate correct removing of port mapping */
} else if(iport==0
|| !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) {
resp[3] = 2; /* Not Authorized/Refused */
} else do {
r = get_redirect_rule(ext_if_name, eport, proto,
iaddr_old, sizeof(iaddr_old),
&iport_old, 0, 0, 0, 0,
&timestamp, 0, 0);
if(r==0) {
if(strcmp(senderaddrstr, iaddr_old)==0
&& iport==iport_old) {
/* redirection allready existing */
syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
/* remove and then add again */
if(_upnp_delete_redir(eport, proto) < 0) {
syslog(LOG_ERR, "failed to remove port mapping");
break;
}
} else {
eport++;
continue;
}
}
{ /* do the redirection */
char desc[64];
#if 0
timestamp = (unsigned)(time(NULL) - startup_time)
+ lifetime;
snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
#else
timestamp = time(NULL) + lifetime;
snprintf(desc, sizeof(desc), "NAT-PMP %hu %s",
eport, (proto==IPPROTO_TCP)?"tcp":"udp");
#endif
/* TODO : check return code */
if(upnp_redirect_internal(NULL, eport, senderaddrstr,
iport, proto, desc,
timestamp) < 0) {
syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
resp[3] = 3; /* Failure */
#if 0
} else if( !nextnatpmptoclean_eport
|| timestamp < nextnatpmptoclean_timestamp) {
nextnatpmptoclean_timestamp = timestamp;
nextnatpmptoclean_eport = eport;
nextnatpmptoclean_proto = proto;
#endif
}
break;
}
} while(r==0);
*((uint16_t *)(resp+8)) = htons(iport); /* private port */
*((uint16_t *)(resp+10)) = htons(eport); /* public port */
*((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */
}
resplen = 16;
break;
default:
resp[3] = 5; /* Unsupported OPCODE */
}
n = sendto(s, resp, resplen, 0,
(struct sockaddr *)&senderaddr, sizeof(senderaddr));
if(n<0) {
syslog(LOG_ERR, "sendto(natpmp): %m");
} else if(n<resplen) {
syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
n, resplen);
}
}
#if 0
/* iterate through the redirection list to find those who came
* from NAT-PMP and select the first to expire */
int ScanNATPMPforExpiration()
{
char desc[64];
unsigned short iport, eport;
int proto;
int r, i;
unsigned timestamp;
nextnatpmptoclean_eport = 0;
nextnatpmptoclean_timestamp = 0;
for(i = 0; ; i++) {
r = get_redirect_rule_by_index(i, 0, &eport, 0, 0,
&iport, &proto, desc, sizeof(desc),
&timestamp, 0, 0);
if(r<0)
break;
if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
if( !nextnatpmptoclean_eport
|| timestamp < nextnatpmptoclean_timestamp) {
nextnatpmptoclean_eport = eport;
nextnatpmptoclean_proto = proto;
nextnatpmptoclean_timestamp = timestamp;
syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp);
}
}
}
return 0;
}
/* remove the next redirection that is expired
*/
int CleanExpiredNATPMP()
{
char desc[64];
unsigned timestamp;
unsigned short iport;
if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport,
nextnatpmptoclean_proto,
0, 0,
&iport, desc, sizeof(desc), &timestamp, 0, 0) < 0)
return ScanNATPMPforExpiration();
/* check desc - this is important since we keep expiration time as part
* of the desc.
* If the rule is renewed, timestamp and nextnatpmptoclean_timestamp
* can be different. In that case, the rule must not be removed ! */
if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
if(timestamp > nextnatpmptoclean_timestamp)
return ScanNATPMPforExpiration();
}
/* remove redirection then search for next one:) */
if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0)
return -1;
syslog(LOG_INFO, "Expired NAT-PMP mapping port %hu %s removed",
nextnatpmptoclean_eport,
nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP");
return ScanNATPMPforExpiration();
}
#endif
/* SendNATPMPPublicAddressChangeNotification()
* should be called when the public IP address changed */
void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
{
struct sockaddr_in sockname;
unsigned char notif[12];
int j, n;
notif[0] = 0; /* vers */
notif[1] = 128; /* OP code */
notif[2] = 0; /* result code */
notif[3] = 0; /* result code */
/* seconds since "start of epoch" :
* time elapsed since the port mapping table was initialized on
* startup or reset for any other reason */
*((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time);
#ifndef MULTIPLE_EXTERNAL_IP
FillPublicAddressResponse(notif, 0);
if(notif[3])
{
syslog(LOG_WARNING, "%s: cannot get public IP address, stopping",
"SendNATPMPPublicAddressChangeNotification");
return;
}
#endif
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR);
for(j=0; j<n_sockets; j++)
{
if(sockets[j] < 0)
continue;
#ifdef MULTIPLE_EXTERNAL_IP
{
struct lan_addr_s * lan_addr = lan_addrs.lh_first;
int i;
for(i=0; i<j; i++)
lan_addr = lan_addr->list.le_next;
FillPublicAddressResponse(notif, lan_addr->addr.s_addr);
}
#endif
/* Port to use in 2006 version of the NAT-PMP specification */
sockname.sin_port = htons(NATPMP_PORT);
n = sendto(sockets[j], notif, 12, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
if(n < 0)
{
syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
"SendNATPMPPublicAddressChangeNotification", sockets[j]);
return;
}
/* Port to use in 2008 version of the NAT-PMP specification */
sockname.sin_port = htons(NATPMP_NOTIF_PORT);
n = sendto(sockets[j], notif, 12, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
if(n < 0)
{
syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
"SendNATPMPPublicAddressChangeNotification", sockets[j]);
return;
}
}
}
#endif

34
miniupnpd/natpmp.h Normal file
View File

@ -0,0 +1,34 @@
/* $Id: natpmp.h,v 1.8 2011/05/27 21:36:22 nanard Exp $ */
/* MiniUPnP project
* author : Thomas Bernard
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*/
#ifndef __NATPMP_H__
#define __NATPMP_H__
/* The NAT-PMP specification which can be found at the url :
* http://files.dns-sd.org/draft-cheshire-nat-pmp.txt
* draft version 3 of April 2008
* define 5351 as listening port for the gateway,
* and the 224.0.0.1 port 5350 as the local link
* multicast address for address change announces.
* Previous versions of the specification defined 5351
* as the port for address change announces. */
#define NATPMP_PORT (5351)
#define NATPMP_NOTIF_PORT (5350)
#define NATPMP_NOTIF_ADDR ("224.0.0.1")
int OpenAndConfNATPMPSockets(int * sockets);
void ProcessIncomingNATPMPPacket(int s);
#if 0
int ScanNATPMPforExpiration(void);
int CleanExpiredNATPMP(void);
#endif
void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets);
#endif

View File

@ -0,0 +1,52 @@
# $Id: Makefile,v 1.3 2011/03/02 16:04:23 nanard Exp $
CFLAGS?=-Wall -g -DDEBUG
CC = gcc
#LIBS = -liptc
LIBS = -lip4tc
ARCH := $(shell uname -m | grep -q "x86_64" && echo 64)
ifdef IPTABLESPATH
CFLAGS := $(CFLAGS) -I$(IPTABLESPATH)/include/
LDFLAGS := $(LDFLAFGS) -L$(IPTABLESPATH)/libiptc/
# get iptables version and set IPTABLES_143 macro if needed
IPTABLESVERSION := $(shell grep "\#define VERSION" $(IPTABLESPATH)/config.h | tr -d \" |cut -d" " -f3 )
IPTABLESVERSION1 := $(shell echo $(IPTABLESVERSION) | cut -d. -f1 )
IPTABLESVERSION2 := $(shell echo $(IPTABLESVERSION) | cut -d. -f2 )
IPTABLESVERSION3 := $(shell echo $(IPTABLESVERSION) | cut -d. -f3 )
# test if iptables version >= 1.4.3
TEST := $(shell [ \( \( $(IPTABLESVERSION1) -ge 1 \) -a \( $(IPTABLESVERSION2) -ge 4 \) \) -a \( $(IPTABLESVERSION3) -ge 3 \) ] && echo 1 )
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
# the following sucks, but works
LIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o
#LIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a
else
LIBS = $(IPTABLESPATH)/libiptc/libiptc.a
endif
else
# check for system-wide iptables files. Test if iptables version >= 1.4.3
TEST := $(shell test -f /usr/include/iptables/internal.h && grep -q "\#define IPTABLES_VERSION" /usr/include/iptables/internal.h && echo 1)
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
LIBS = -liptc
TEST_LIB := $(shell test -f /usr/lib$(ARCH)/libiptc.a && echo 1)
ifeq ($(TEST_LIB), 1)
LIBS = -liptc /usr/lib$(ARCH)/libiptc.a
endif
endif
endif
all: iptcrdr.o testiptcrdr
clean:
$(RM) *.o testiptcrdr
testiptcrdr: testiptcrdr.o iptcrdr.o upnpglobalvars.o $(LIBS)
iptcrdr.o: iptcrdr.c iptcrdr.h
upnpglobalvars.o: ../upnpglobalvars.c ../upnpglobalvars.h
$(CC) -c -o $@ $<

View File

@ -0,0 +1,10 @@
#! /bin/sh
# $Id: iptables_display.sh,v 1.4 2011/05/16 12:11:37 nanard Exp $
IPTABLES=/sbin/iptables
#display all chains relative to miniupnpd
$IPTABLES -v -n -t nat -L PREROUTING
$IPTABLES -v -n -t nat -L MINIUPNPD
$IPTABLES -v -n -t filter -L FORWARD
$IPTABLES -v -n -t filter -L MINIUPNPD

View File

@ -0,0 +1,8 @@
#! /bin/sh
# $Id: iptables_flush.sh,v 1.3 2011/05/16 12:11:37 nanard Exp $
IPTABLES=/sbin/iptables
#flush all rules owned by miniupnpd
$IPTABLES -t nat -F MINIUPNPD
$IPTABLES -t filter -F MINIUPNPD

View File

@ -0,0 +1,20 @@
#! /bin/sh
# $Id: iptables_init.sh,v 1.5 2011/05/16 12:11:37 nanard Exp $
IPTABLES=/sbin/iptables
#change this parameters :
EXTIF=eth0
EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`"
echo "External IP = $EXTIP"
#adding the MINIUPNPD chain for nat
$IPTABLES -t nat -N MINIUPNPD
#adding the rule to MINIUPNPD
#$IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD
$IPTABLES -t nat -A PREROUTING -i $EXTIF -j MINIUPNPD
#adding the MINIUPNPD chain for filter
$IPTABLES -t filter -N MINIUPNPD
#adding the rule to MINIUPNPD
$IPTABLES -t filter -A FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD

View File

@ -0,0 +1,37 @@
#! /bin/sh
# $Id: iptables_init_and_clean.sh,v 1.1 2011/05/13 09:58:47 nanard Exp $
# Improved Miniupnpd iptables init script.
# Checks for state of filter before doing anything..
EXTIF=eth0
IPTABLES=/sbin/iptables
EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`"
NDIRTY="`LC_ALL=C /sbin/iptables -t nat -L -n | grep 'MINIUPNPD' | awk '{printf $1}'`"
FDIRTY="`LC_ALL=C /sbin/iptables -t filter -L -n | grep 'MINIUPNPD' | awk '{printf $1}'`"
echo "External IP = $EXTIP"
if [[ $NDIRTY = "MINIUPNPDChain" ]]; then
echo "Nat table dirty; Cleaning..."
$IPTABLES -t nat -F MINIUPNPD
elif [[ $NDIRTY = "Chain" ]]; then
echo "Dirty NAT chain but no reference..? Fixsted."
$IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD
$IPTABLES -t nat -F MINIUPNPD
else
echo "NAT table clean..initalizing.."
$IPTABLES -t nat -N MINIUPNPD
$IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD
fi
if [[ $FDIRTY = "MINIUPNPDChain" ]]; then
echo "Filter table dirty; Cleaning..."
$IPTABLES -t filter -F MINIUPNPD
elif [[ $FDIRTY = "Chain" ]]; then
echo "Dirty filter chain but no reference..? Fixsted."
$IPTABLES -t filter -I FORWARD 4 -i $EXTIF ! -o $EXTIF -j MINIUPNPD
$IPTABLES -t filter -F MINIUPNPD
else
echo "Filter table clean..initalizing.."
$IPTABLES -t filter -N MINIUPNPD
$IPTABLES -t filter -I FORWARD 4 -i $EXTIF ! -o $EXTIF -j MINIUPNPD
fi

View File

@ -0,0 +1,21 @@
#! /bin/sh
# $Id: iptables_removeall.sh,v 1.5 2011/05/16 12:11:37 nanard Exp $
IPTABLES=/sbin/iptables
#change this parameters :
EXTIF=eth0
EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`"
#removing the MINIUPNPD chain for nat
$IPTABLES -t nat -F MINIUPNPD
#rmeoving the rule to MINIUPNPD
#$IPTABLES -t nat -D PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD
$IPTABLES -t nat -D PREROUTING -i $EXTIF -j MINIUPNPD
$IPTABLES -t nat -X MINIUPNPD
#removing the MINIUPNPD chain for filter
$IPTABLES -t filter -F MINIUPNPD
#adding the rule to MINIUPNPD
$IPTABLES -t filter -D FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD
$IPTABLES -t filter -X MINIUPNPD

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
/* $Id: iptcrdr.h,v 1.17 2011/06/04 15:44:58 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __IPTCRDR_H__
#define __IPTCRDR_H__
#include "../commonrdr.h"
int
add_redirect_rule2(const char * ifname,
const char * rhost, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp);
int
add_filter_rule2(const char * ifname,
const char * rhost, const char * iaddr,
unsigned short eport, unsigned short iport,
int proto, const char * desc);
int
delete_redirect_and_filter_rules(unsigned short eport, int proto);
/* for debug */
int
list_redirect_rule(const char * ifname);
#endif

View File

@ -0,0 +1,63 @@
/* $Id: testiptcrdr.c,v 1.16 2011/03/02 16:04:23 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <syslog.h>
#include "iptcrdr.h"
#ifndef PRIu64
#define PRIu64 "llu"
#endif
int
main(int argc, char ** argv)
{
unsigned short eport, iport;
const char * iaddr;
printf("Usage %s <ext_port> <internal_ip> <internal_port>\n", argv[0]);
if(argc<4)
return -1;
openlog("testiptcrdr", LOG_PERROR|LOG_CONS, LOG_LOCAL0);
eport = (unsigned short)atoi(argv[1]);
iaddr = argv[2];
iport = (unsigned short)atoi(argv[3]);
printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport);
if(addnatrule(IPPROTO_TCP, eport, iaddr, iport) < 0)
return -1;
if(add_filter_rule(IPPROTO_TCP, iaddr, iport) < 0)
return -1;
/* test */
{
unsigned short p1, p2;
char addr[16];
int proto2;
char desc[256];
u_int64_t packets, bytes;
desc[0] = '\0';
if(get_redirect_rule_by_index(0, "", &p1, addr, sizeof(addr),
&p2, &proto2, desc, sizeof(desc),
&packets, &bytes) < 0)
{
printf("rule not found\n");
}
else
{
printf("redirected port %hu to %s:%hu proto %d packets=%" PRIu64 " bytes=%" PRIu64 "\n",
p1, addr, p2, proto2, packets, bytes);
}
}
printf("trying to list nat rules :\n");
list_redirect_rule(argv[1]);
printf("deleting\n");
delete_redirect_and_filter_rules(eport, IPPROTO_TCP);
return 0;
}

View File

@ -0,0 +1,37 @@
/* $Id: tiny_nf_nat.h,v 1.1 2011/07/30 13:14:36 nanard Exp $ */
/* Only what miniupnpd needs, until linux-libc-dev gains nf_nat.h */
#ifndef TINY_NF_NAT_H
#define TINY_NF_NAT_H
#include <linux/types.h>
#define IP_NAT_RANGE_MAP_IPS 1
#define IP_NAT_RANGE_PROTO_SPECIFIED 2
#define IP_NAT_RANGE_PROTO_RANDOM 4
#define IP_NAT_RANGE_PERSISTENT 8
union nf_conntrack_man_proto {
__be16 all;
struct { __be16 port; } tcp;
struct { __be16 port; } udp;
struct { __be16 id; } icmp;
struct { __be16 port; } dccp;
struct { __be16 port; } sctp;
struct { __be16 key; } gre;
};
struct nf_nat_range {
unsigned int flags;
__be32 min_ip, max_ip;
union nf_conntrack_man_proto min, max;
};
struct nf_nat_multi_range_compat {
unsigned int rangesize;
struct nf_nat_range range[1];
};
#define nf_nat_multi_range nf_nat_multi_range_compat
#endif /*TINY_NF_NAT_H*/

195
miniupnpd/options.c Normal file
View File

@ -0,0 +1,195 @@
/* $Id: options.c,v 1.20 2008/10/06 13:22:02 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <syslog.h>
#include "options.h"
#include "upnppermissions.h"
#include "upnpglobalvars.h"
struct option * ary_options = NULL;
int num_options = 0;
static const struct {
enum upnpconfigoptions id;
const char * name;
} optionids[] = {
{ UPNPEXT_IFNAME, "ext_ifname" },
{ UPNPEXT_IP, "ext_ip" },
{ UPNPLISTENING_IP, "listening_ip" },
{ UPNPPORT, "port" },
{ UPNPBITRATE_UP, "bitrate_up" },
{ UPNPBITRATE_DOWN, "bitrate_down" },
{ UPNPPRESENTATIONURL, "presentation_url" },
{ UPNPNOTIFY_INTERVAL, "notify_interval" },
{ UPNPSYSTEM_UPTIME, "system_uptime" },
{ UPNPPACKET_LOG, "packet_log" },
{ UPNPUUID, "uuid"},
{ UPNPSERIAL, "serial"},
{ UPNPMODEL_NUMBER, "model_number"},
{ UPNPCLEANTHRESHOLD, "clean_ruleset_threshold"},
{ UPNPCLEANINTERVAL, "clean_ruleset_interval"},
#ifdef USE_NETFILTER
{ UPNPFORWARDCHAIN, "upnp_forward_chain"},
{ UPNPNATCHAIN, "upnp_nat_chain"},
#endif
#ifdef ENABLE_NATPMP
{ UPNPENABLENATPMP, "enable_natpmp"},
#endif
{ UPNPENABLE, "enable_upnp"},
#ifdef USE_PF
{ UPNPQUEUE, "queue"},
{ UPNPTAG, "tag"},
#endif
#ifdef PF_ENABLE_FILTER_RULES
{ UPNPQUICKRULES, "quickrules" },
#endif
#ifdef ENABLE_LEASEFILE
{ UPNPLEASEFILE, "lease_file"},
#endif
{ UPNPMINISSDPDSOCKET, "minissdpdsocket"},
{ UPNPSECUREMODE, "secure_mode"}
};
int
readoptionsfile(const char * fname)
{
FILE *hfile = NULL;
char buffer[1024];
char *equals;
char *name;
char *value;
char *t;
int linenum = 0;
int i;
enum upnpconfigoptions id;
if(!fname || (strlen(fname) == 0))
return -1;
memset(buffer, 0, sizeof(buffer));
#ifdef DEBUG
printf("Reading configuration from file %s\n", fname);
#endif
if(!(hfile = fopen(fname, "r")))
return -1;
if(ary_options != NULL)
{
free(ary_options);
num_options = 0;
}
while(fgets(buffer, sizeof(buffer), hfile))
{
linenum++;
t = strchr(buffer, '\n');
if(t)
{
*t = '\0';
t--;
while((t >= buffer) && isspace(*t))
{
*t = '\0';
t--;
}
}
/* skip leading whitespaces */
name = buffer;
while(isspace(*name))
name++;
/* check for comments or empty lines */
if(name[0] == '#' || name[0] == '\0') continue;
/* check for UPnP permissions rule */
if(0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4))
{
upnppermlist = realloc(upnppermlist,
sizeof(struct upnpperm) * (num_upnpperm+1));
/* parse the rule */
if(read_permission_line(upnppermlist + num_upnpperm, name) >= 0)
{
num_upnpperm++;
}
else
{
fprintf(stderr, "parsing error file %s line %d : %s\n",
fname, linenum, name);
}
continue;
}
if(!(equals = strchr(name, '=')))
{
fprintf(stderr, "parsing error file %s line %d : %s\n",
fname, linenum, name);
continue;
}
/* remove ending whitespaces */
for(t=equals-1; t>name && isspace(*t); t--)
*t = '\0';
*equals = '\0';
value = equals+1;
/* skip leading whitespaces */
while(isspace(*value))
value++;
id = UPNP_INVALID;
for(i=0; i<sizeof(optionids)/sizeof(optionids[0]); i++)
{
/*printf("%2d %2d %s %s\n", i, optionids[i].id, name,
optionids[i].name); */
if(0 == strcmp(name, optionids[i].name))
{
id = optionids[i].id;
break;
}
}
if(id == UPNP_INVALID)
{
fprintf(stderr, "parsing error file %s line %d : %s=%s\n",
fname, linenum, name, value);
}
else
{
num_options += 1;
ary_options = (struct option *) realloc(ary_options, num_options * sizeof(struct option));
ary_options[num_options-1].id = id;
strncpy(ary_options[num_options-1].value, value, MAX_OPTION_VALUE_LEN);
}
}
fclose(hfile);
return 0;
}
void
freeoptions(void)
{
if(ary_options)
{
free(ary_options);
ary_options = NULL;
num_options = 0;
}
}

74
miniupnpd/options.h Normal file
View File

@ -0,0 +1,74 @@
/* $Id: options.h,v 1.15 2008/10/06 13:22:02 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __OPTIONS_H__
#define __OPTIONS_H__
#include "config.h"
/* enum of option available in the miniupnpd.conf */
enum upnpconfigoptions {
UPNP_INVALID = 0,
UPNPEXT_IFNAME = 1, /* ext_ifname */
UPNPEXT_IP, /* ext_ip */
UPNPLISTENING_IP, /* listening_ip */
UPNPPORT, /* "port" */
UPNPBITRATE_UP, /* "bitrate_up" */
UPNPBITRATE_DOWN, /* "bitrate_down" */
UPNPPRESENTATIONURL, /* presentation_url */
UPNPNOTIFY_INTERVAL, /* notify_interval */
UPNPSYSTEM_UPTIME, /* "system_uptime" */
UPNPPACKET_LOG, /* "packet_log" */
UPNPUUID, /* uuid */
UPNPSERIAL, /* serial */
UPNPMODEL_NUMBER, /* model_number */
UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */
UPNPCLEANINTERVAL, /* clean_ruleset_interval */
UPNPENABLENATPMP, /* enable_natpmp */
#ifdef USE_NETFILTER
UPNPFORWARDCHAIN,
UPNPNATCHAIN,
#endif
#ifdef USE_PF
UPNPQUEUE, /* queue */
UPNPTAG, /* tag */
#endif
#ifdef PF_ENABLE_FILTER_RULES
UPNPQUICKRULES, /* quickrules */
#endif
UPNPSECUREMODE, /* secure_mode */
#ifdef ENABLE_LEASEFILE
UPNPLEASEFILE, /* lease_file */
#endif
UPNPMINISSDPDSOCKET, /* minissdpdsocket */
UPNPENABLE /* enable_upnp */
};
/* readoptionsfile()
* parse and store the option file values
* returns: 0 success, -1 failure */
int
readoptionsfile(const char * fname);
/* freeoptions()
* frees memory allocated to option values */
void
freeoptions(void);
#define MAX_OPTION_VALUE_LEN (80)
struct option
{
enum upnpconfigoptions id;
char value[MAX_OPTION_VALUE_LEN];
};
extern struct option * ary_options;
extern int num_options;
#endif

17
miniupnpd/pf/Makefile Normal file
View File

@ -0,0 +1,17 @@
# $Id: Makefile,v 1.2 2010/03/08 17:10:37 nanard Exp $
# made for GNU Make (and BSD make)
CFLAGS = -Wall -g -DTEST
EXECUTABLES = testobsdrdr
all: $(EXECUTABLES)
clean:
rm -f *.o $(EXECUTABLES)
testobsdrdr: testobsdrdr.o obsdrdr.o
$(CC) $(CFLAGS) -o $@ $>
obsdrdr.o: obsdrdr.c obsdrdr.h
testobsdrdr.o: testobsdrdr.c obsdrdr.h

910
miniupnpd/pf/obsdrdr.c Normal file
View File

@ -0,0 +1,910 @@
/* $Id: obsdrdr.c,v 1.67 2011/06/22 21:20:27 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2010 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
/*
* pf rules created (with ext_if = xl1)
* - OpenBSD up to version 4.6 :
* rdr pass on xl1 inet proto udp from any to any port = 54321 \
* label "test label" -> 192.168.0.141 port 12345
* or a rdr rule + a pass rule
*
* - OpenBSD starting from version 4.7
* match in on xl1 inet proto udp from any to any port 54321 \
* label "test label" rdr-to 192.168.0.141 port 12345
* or
* pass in quick on xl1 inet proto udp from any to any port 54321 \
* label "test label" rdr-to 192.168.0.141 port 12345
*
*
*
* Macros/#defines :
* - PF_ENABLE_FILTER_RULES
* If set, two rules are created : rdr + pass. Else a rdr/pass rule
* is created.
* - USE_IFNAME_IN_RULES
* If set the interface name is set in the rule.
* - PFRULE_INOUT_COUNTS
* Must be set with OpenBSD version 3.8 and up.
* - PFRULE_HAS_RTABLEID
* Must be set with OpenBSD version 4.0 and up.
* - PF_NEWSSTYLE
* Must be set with OpenBSD version 4.7 and up.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#ifdef __DragonFly__
#include <net/pf/pfvar.h>
#else
#include <net/pfvar.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include "../config.h"
#include "obsdrdr.h"
#include "../upnpglobalvars.h"
/* list too keep timestamps for port mappings having a lease duration */
struct timestamp_entry {
struct timestamp_entry * next;
unsigned int timestamp;
unsigned short eport;
short protocol;
};
static struct timestamp_entry * timestamp_list = NULL;
static unsigned int
get_timestamp(unsigned short eport, int proto)
{
struct timestamp_entry * e;
e = timestamp_list;
while(e) {
if(e->eport == eport && e->protocol == (short)proto)
return e->timestamp;
e = e->next;
}
return 0;
}
static void
remove_timestamp_entry(unsigned short eport, int proto)
{
struct timestamp_entry * e;
struct timestamp_entry * * p;
p = &timestamp_list;
e = *p;
while(e) {
if(e->eport == eport && e->protocol == (short)proto) {
/* remove the entry */
*p = e->next;
free(e);
return;
}
p = &(e->next);
e = *p;
}
}
/* anchor name */
static const char anchor_name[] = "miniupnpd";
/* /dev/pf when opened */
static int dev = -1;
/* shutdown_redirect() :
* close the /dev/pf device */
void
shutdown_redirect(void)
{
if(close(dev)<0)
syslog(LOG_ERR, "close(\"/dev/pf\"): %m");
dev = -1;
}
/* open the device */
int
init_redirect(void)
{
struct pf_status status;
if(dev>=0)
shutdown_redirect();
dev = open("/dev/pf", O_RDWR);
if(dev<0) {
syslog(LOG_ERR, "open(\"/dev/pf\"): %m");
return -1;
}
if(ioctl(dev, DIOCGETSTATUS, &status)<0) {
syslog(LOG_ERR, "DIOCGETSTATUS: %m");
return -1;
}
if(!status.running) {
syslog(LOG_ERR, "pf is disabled");
return -1;
}
return 0;
}
#if TEST
/* for debug */
int
clear_redirect_rules(void)
{
struct pfioc_trans io;
struct pfioc_trans_e ioe;
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
memset(&ioe, 0, sizeof(ioe));
io.size = 1;
io.esize = sizeof(ioe);
io.array = &ioe;
#ifndef PF_NEWSTYLE
ioe.rs_num = PF_RULESET_RDR;
#else
ioe.type = PF_TRANS_RULESET;
#endif
strlcpy(ioe.anchor, anchor_name, MAXPATHLEN);
if(ioctl(dev, DIOCXBEGIN, &io) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m");
goto error;
}
if(ioctl(dev, DIOCXCOMMIT, &io) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m");
goto error;
}
return 0;
error:
return -1;
}
#endif
/* add_redirect_rule2() :
* create a rdr rule */
int
add_redirect_rule2(const char * ifname,
const char * rhost, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp)
{
int r;
struct pfioc_rule pcr;
#ifndef PF_NEWSTYLE
struct pfioc_pooladdr pp;
struct pf_pooladdr *a;
#endif
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
r = 0;
memset(&pcr, 0, sizeof(pcr));
strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
#ifndef PF_NEWSTYLE
memset(&pp, 0, sizeof(pp));
strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
r = -1;
}
else
{
pcr.pool_ticket = pp.ticket;
#else
if(1)
{
pcr.rule.direction = PF_IN;
//pcr.rule.src.addr.type = PF_ADDR_NONE;
pcr.rule.src.addr.type = PF_ADDR_ADDRMASK;
pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
pcr.rule.nat.addr.type = PF_ADDR_NONE;
pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
#endif
pcr.rule.dst.port_op = PF_OP_EQ;
pcr.rule.dst.port[0] = htons(eport);
pcr.rule.dst.port[1] = htons(eport);
#ifndef PF_NEWSTYLE
pcr.rule.action = PF_RDR;
#ifndef PF_ENABLE_FILTER_RULES
pcr.rule.natpass = 1;
#else
pcr.rule.natpass = 0;
#endif
#else
#ifndef PF_ENABLE_FILTER_RULES
pcr.rule.action = PF_PASS;
#else
pcr.rule.action = PF_MATCH;
#endif
#endif
pcr.rule.af = AF_INET;
#ifdef USE_IFNAME_IN_RULES
if(ifname)
strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
#endif
pcr.rule.proto = proto;
pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
#ifdef PFRULE_HAS_RTABLEID
pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
#endif
pcr.rule.quick = 1;
pcr.rule.keep_state = PF_STATE_NORMAL;
if(tag)
strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
if(rhost && rhost[0] != '\0' && rhost[0] != '*')
{
inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
}
#ifndef PF_NEWSTYLE
pcr.rule.rpool.proxy_port[0] = iport;
pcr.rule.rpool.proxy_port[1] = iport;
TAILQ_INIT(&pcr.rule.rpool.list);
a = calloc(1, sizeof(struct pf_pooladdr));
inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries);
memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
if(ioctl(dev, DIOCADDADDR, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m");
r = -1;
}
else
{
#else
pcr.rule.rdr.proxy_port[0] = iport;
pcr.rule.rdr.proxy_port[1] = iport;
inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr);
pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
if(1)
{
#endif
pcr.action = PF_CHANGE_GET_TICKET;
if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
r = -1;
}
else
{
pcr.action = PF_CHANGE_ADD_TAIL;
if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
r = -1;
}
}
}
#ifndef PF_NEWSTYLE
free(a);
#endif
}
if(r == 0 && timestamp > 0)
{
struct timestamp_entry * tmp;
tmp = malloc(sizeof(struct timestamp_entry));
if(tmp)
{
tmp->next = timestamp_list;
tmp->timestamp = timestamp;
tmp->eport = eport;
tmp->protocol = (short)proto;
timestamp_list = tmp;
}
}
return r;
}
/* thanks to Seth Mos for this function */
int
add_filter_rule2(const char * ifname,
const char * rhost, const char * iaddr,
unsigned short eport, unsigned short iport,
int proto, const char * desc)
{
#ifndef PF_ENABLE_FILTER_RULES
return 0;
#else
int r;
struct pfioc_rule pcr;
#ifndef PF_NEWSTYLE
struct pfioc_pooladdr pp;
struct pf_pooladdr *a;
#endif
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
r = 0;
memset(&pcr, 0, sizeof(pcr));
strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
#ifndef PF_NEWSTYLE
memset(&pp, 0, sizeof(pp));
strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
r = -1;
}
else
{
pcr.pool_ticket = pp.ticket;
#else
if(1)
{
#endif
pcr.rule.dst.port_op = PF_OP_EQ;
pcr.rule.dst.port[0] = htons(eport);
pcr.rule.direction = PF_IN;
pcr.rule.action = PF_PASS;
pcr.rule.af = AF_INET;
#ifdef USE_IFNAME_IN_RULES
if(ifname)
strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
#endif
pcr.rule.proto = proto;
pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1;
pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
/* see the discussion on the forum :
* http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */
pcr.rule.flags = TH_SYN;
pcr.rule.flagset = (TH_SYN|TH_ACK);
#ifdef PFRULE_HAS_RTABLEID
pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
#endif
pcr.rule.keep_state = 1;
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
if(queue)
strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE);
if(tag)
strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
if(rhost && rhost[0] != '\0' && rhost[0] != '*')
{
inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
}
#ifndef PF_NEWSTYLE
pcr.rule.rpool.proxy_port[0] = eport;
a = calloc(1, sizeof(struct pf_pooladdr));
inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
TAILQ_INIT(&pcr.rule.rpool.list);
inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries);
/* we have any - any port = # keep state label */
/* we want any - iaddr port = # keep state label */
/* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */
memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
if(ioctl(dev, DIOCADDADDR, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m");
r = -1;
}
else
{
#else
if(1)
{
#endif
pcr.action = PF_CHANGE_GET_TICKET;
if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
r = -1;
}
else
{
pcr.action = PF_CHANGE_ADD_TAIL;
if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
r = -1;
}
}
}
#ifndef PF_NEWSTYLE
free(a);
#endif
}
return r;
#endif
}
/* get_redirect_rule()
* return value : 0 success (found)
* -1 = error or rule not found */
int
get_redirect_rule(const char * ifname, unsigned short eport, int proto,
char * iaddr, int iaddrlen, unsigned short * iport,
char * desc, int desclen,
char * rhost, int rhostlen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
int i, n;
struct pfioc_rule pr;
#ifndef PF_NEWSTYLE
struct pfioc_pooladdr pp;
#endif
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
memset(&pr, 0, sizeof(pr));
strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
#ifndef PF_NEWSTYLE
pr.rule.action = PF_RDR;
#endif
if(ioctl(dev, DIOCGETRULES, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
goto error;
}
n = pr.nr;
for(i=0; i<n; i++)
{
pr.nr = i;
if(ioctl(dev, DIOCGETRULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
goto error;
}
if( (eport == ntohs(pr.rule.dst.port[0]))
&& (eport == ntohs(pr.rule.dst.port[1]))
&& (pr.rule.proto == proto) )
{
#ifndef PF_NEWSTYLE
*iport = pr.rule.rpool.proxy_port[0];
#else
*iport = pr.rule.rdr.proxy_port[0];
#endif
if(desc)
strlcpy(desc, pr.rule.label, desclen);
#ifdef PFRULE_INOUT_COUNTS
if(packets)
*packets = pr.rule.packets[0] + pr.rule.packets[1];
if(bytes)
*bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
#else
if(packets)
*packets = pr.rule.packets;
if(bytes)
*bytes = pr.rule.bytes;
#endif
#ifndef PF_NEWSTYLE
memset(&pp, 0, sizeof(pp));
strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
pp.r_action = PF_RDR;
pp.r_num = i;
pp.ticket = pr.ticket;
if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
goto error;
}
if(pp.nr != 1)
{
syslog(LOG_NOTICE, "No address associated with pf rule");
goto error;
}
pp.nr = 0; /* first */
if(ioctl(dev, DIOCGETADDR, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
goto error;
}
inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
iaddr, iaddrlen);
#else
inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
iaddr, iaddrlen);
#endif
if(rhost && rhostlen > 0)
{
if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
{
rhost[0] = '\0'; /* empty string */
}
else
{
inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
rhost, rhostlen);
}
}
if(timestamp)
*timestamp = get_timestamp(eport, proto);
return 0;
}
}
error:
return -1;
}
int
delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
{
int i, n;
struct pfioc_rule pr;
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
memset(&pr, 0, sizeof(pr));
strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
#ifndef PF_NEWSTYLE
pr.rule.action = PF_RDR;
#endif
if(ioctl(dev, DIOCGETRULES, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
goto error;
}
n = pr.nr;
for(i=0; i<n; i++)
{
pr.nr = i;
if(ioctl(dev, DIOCGETRULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
goto error;
}
if( (eport == ntohs(pr.rule.dst.port[0]))
&& (eport == ntohs(pr.rule.dst.port[1]))
&& (pr.rule.proto == proto) )
{
pr.action = PF_CHANGE_GET_TICKET;
if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
goto error;
}
pr.action = PF_CHANGE_REMOVE;
pr.nr = i;
if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
goto error;
}
remove_timestamp_entry(eport, proto);
return 0;
}
}
error:
return -1;
}
int
delete_filter_rule(const char * ifname, unsigned short eport, int proto)
{
#ifndef PF_ENABLE_FILTER_RULES
return 0;
#else
int i, n;
struct pfioc_rule pr;
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
memset(&pr, 0, sizeof(pr));
strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
pr.rule.action = PF_PASS;
if(ioctl(dev, DIOCGETRULES, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
goto error;
}
n = pr.nr;
for(i=0; i<n; i++)
{
pr.nr = i;
if(ioctl(dev, DIOCGETRULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
goto error;
}
if( (eport == ntohs(pr.rule.dst.port[0]))
&& (pr.rule.proto == proto) )
{
pr.action = PF_CHANGE_GET_TICKET;
if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
goto error;
}
pr.action = PF_CHANGE_REMOVE;
pr.nr = i;
if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
goto error;
}
return 0;
}
}
error:
return -1;
#endif
}
int
get_redirect_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
char * rhost, int rhostlen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
int n;
struct pfioc_rule pr;
#ifndef PF_NEWSTYLE
struct pfioc_pooladdr pp;
#endif
if(index < 0)
return -1;
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return -1;
}
memset(&pr, 0, sizeof(pr));
strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
#ifndef PF_NEWSTYLE
pr.rule.action = PF_RDR;
#endif
if(ioctl(dev, DIOCGETRULES, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
goto error;
}
n = pr.nr;
if(index >= n)
goto error;
pr.nr = index;
if(ioctl(dev, DIOCGETRULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
goto error;
}
*proto = pr.rule.proto;
*eport = ntohs(pr.rule.dst.port[0]);
#ifndef PF_NEWSTYLE
*iport = pr.rule.rpool.proxy_port[0];
#else
*iport = pr.rule.rdr.proxy_port[0];
#endif
if(ifname)
strlcpy(ifname, pr.rule.ifname, IFNAMSIZ);
if(desc)
strlcpy(desc, pr.rule.label, desclen);
#ifdef PFRULE_INOUT_COUNTS
if(packets)
*packets = pr.rule.packets[0] + pr.rule.packets[1];
if(bytes)
*bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
#else
if(packets)
*packets = pr.rule.packets;
if(bytes)
*bytes = pr.rule.bytes;
#endif
#ifndef PF_NEWSTYLE
memset(&pp, 0, sizeof(pp));
strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
pp.r_action = PF_RDR;
pp.r_num = index;
pp.ticket = pr.ticket;
if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
goto error;
}
if(pp.nr != 1)
{
syslog(LOG_NOTICE, "No address associated with pf rule");
goto error;
}
pp.nr = 0; /* first */
if(ioctl(dev, DIOCGETADDR, &pp) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
goto error;
}
inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
iaddr, iaddrlen);
#else
inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
iaddr, iaddrlen);
#endif
if(rhost && rhostlen > 0)
{
if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
{
rhost[0] = '\0'; /* empty string */
}
else
{
inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
rhost, rhostlen);
}
}
if(timestamp)
*timestamp = get_timestamp(*eport, *proto);
return 0;
error:
return -1;
}
/* return an (malloc'ed) array of "external" port for which there is
* a port mapping. number is the size of the array */
unsigned short *
get_portmappings_in_range(unsigned short startport, unsigned short endport,
int proto, unsigned int * number)
{
unsigned short * array;
unsigned int capacity;
int i, n;
unsigned short eport;
struct pfioc_rule pr;
*number = 0;
if(dev<0) {
syslog(LOG_ERR, "pf device is not open");
return NULL;
}
capacity = 128;
array = calloc(capacity, sizeof(unsigned short));
if(!array)
{
syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
return NULL;
}
memset(&pr, 0, sizeof(pr));
strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
#ifndef PF_NEWSTYLE
pr.rule.action = PF_RDR;
#endif
if(ioctl(dev, DIOCGETRULES, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
free(array);
return NULL;
}
n = pr.nr;
for(i=0; i<n; i++)
{
pr.nr = i;
if(ioctl(dev, DIOCGETRULE, &pr) < 0)
{
syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
continue;
}
eport = ntohs(pr.rule.dst.port[0]);
if( (eport == ntohs(pr.rule.dst.port[1]))
&& (pr.rule.proto == proto)
&& (startport <= eport) && (eport <= endport) )
{
if(*number >= capacity)
{
/* need to increase the capacity of the array */
capacity += 128;
array = realloc(array, sizeof(unsigned short)*capacity);
if(!array)
{
syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
*number = 0;
return NULL;
}
}
array[*number] = eport;
(*number)++;
}
}
return array;
}
/* this function is only for testing */
#if TEST
void
list_rules(void)
{
char buf[32];
int i, n;
struct pfioc_rule pr;
#ifndef PF_NEWSTYLE
struct pfioc_pooladdr pp;
#endif
if(dev<0)
{
perror("pf dev not open");
return ;
}
memset(&pr, 0, sizeof(pr));
strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
pr.rule.action = PF_RDR;
if(ioctl(dev, DIOCGETRULES, &pr) < 0)
perror("DIOCGETRULES");
printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr);
n = pr.nr;
for(i=0; i<n; i++)
{
printf("-- rule %d --\n", i);
pr.nr = i;
if(ioctl(dev, DIOCGETRULE, &pr) < 0)
perror("DIOCGETRULE");
printf(" %s %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n",
pr.rule.ifname,
inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32);
(int)ntohs(pr.rule.dst.port[0]),
(int)ntohs(pr.rule.dst.port[1]),
#ifndef PF_NEWSTYLE
(int)pr.rule.rpool.proxy_port[0],
(int)pr.rule.rpool.proxy_port[1],
#else
(int)pr.rule.rdr.proxy_port[0],
(int)pr.rule.rdr.proxy_port[1],
#endif
(int)pr.rule.proto,
(int)pr.rule.keep_state,
(int)pr.rule.action);
printf(" description: \"%s\"\n", pr.rule.label);
#ifndef PF_NEWSTYLE
memset(&pp, 0, sizeof(pp));
strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
pp.r_action = PF_RDR;
pp.r_num = i;
pp.ticket = pr.ticket;
if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
perror("DIOCGETADDRS");
printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket);
/*if(ioctl(dev, DIOCGETRULE, &pr) < 0)
perror("DIOCGETRULE"); */
pp.nr = 0; /* first */
if(ioctl(dev, DIOCGETADDR, &pp) < 0)
perror("DIOCGETADDR");
/* addr.v.a.addr.v4.s_addr */
printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32));
#else
printf(" rule_flag=%08x action=%d direction=%d log=%d logif=%d "
"quick=%d ifnot=%d af=%d type=%d code=%d rdr.port_op=%d rdr.opts=%d\n",
pr.rule.rule_flag, pr.rule.action, pr.rule.direction,
pr.rule.log, pr.rule.logif, pr.rule.quick, pr.rule.ifnot,
pr.rule.af, pr.rule.type, pr.rule.code,
pr.rule.rdr.port_op, pr.rule.rdr.opts);
printf(" %s\n", inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, buf, 32));
#endif
}
}
#endif

65
miniupnpd/pf/obsdrdr.h Normal file
View File

@ -0,0 +1,65 @@
/* $Id: obsdrdr.h,v 1.19 2011/06/04 15:47:18 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __OBSDRDR_H__
#define __OBSDRDR_H__
#include "../commonrdr.h"
/* add_redirect_rule2() uses DIOCCHANGERULE ioctl
* proto can take the values IPPROTO_UDP or IPPROTO_TCP
*/
int
add_redirect_rule2(const char * ifname,
const char * rhost, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp);
/* add_filter_rule2() uses DIOCCHANGERULE ioctl
* proto can take the values IPPROTO_UDP or IPPROTO_TCP
*/
int
add_filter_rule2(const char * ifname,
const char * rhost, const char * iaddr,
unsigned short eport, unsigned short iport,
int proto, const char * desc);
/* get_redirect_rule() gets internal IP and port from
* interface, external port and protocl
*/
#if 0
int
get_redirect_rule(const char * ifname, unsigned short eport, int proto,
char * iaddr, int iaddrlen, unsigned short * iport,
char * desc, int desclen,
u_int64_t * packets, u_int64_t * bytes);
int
get_redirect_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
u_int64_t * packets, u_int64_t * bytes);
#endif
/* delete_redirect_rule()
*/
int
delete_redirect_rule(const char * ifname, unsigned short eport, int proto);
/* delete_filter_rule()
*/
int
delete_filter_rule(const char * ifname, unsigned short eport, int proto);
int
clear_redirect_rules(void);
#endif

125
miniupnpd/pf/testobsdrdr.c Normal file
View File

@ -0,0 +1,125 @@
/* $Id: testobsdrdr.c,v 1.22 2011/06/04 16:45:22 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <syslog.h>
#include "obsdrdr.h"
/*int logpackets = 1;*/
int runtime_flags = 0;
const char * tag = 0;
void
list_rules(void);
void
list_eports_tcp(void)
{
unsigned short * port_list;
unsigned int number = 0;
unsigned int i;
port_list = get_portmappings_in_range(0, 65535, IPPROTO_TCP, &number);
printf("%u ports redirected (TCP) :", number);
for(i = 0; i < number; i++)
{
printf(" %hu", port_list[i]);
}
printf("\n");
free(port_list);
}
void
test_index(void)
{
char ifname[16/*IFNAMSIZ*/];
char iaddr[32];
char desc[64];
char rhost[32];
unsigned short iport = 0;
unsigned short eport = 0;
int proto = 0;
unsigned int timestamp;
ifname[0] = '\0';
iaddr[0] = '\0';
rhost[0] = '\0';
if(get_redirect_rule_by_index(0, ifname, &eport, iaddr, sizeof(iaddr),
&iport, &proto, desc, sizeof(desc),
rhost, sizeof(rhost),
&timestamp, 0, 0) < 0)
{
printf("get.._by_index : no rule\n");
}
else
{
printf("%s %u -> %s:%u proto %d\n", ifname, (unsigned int)eport,
iaddr, (unsigned int)iport, proto);
printf("description: \"%s\"\n", desc);
}
}
int
main(int arc, char * * argv)
{
char buf[32];
char desc[64];
/*char rhost[32];*/
unsigned short iport;
unsigned int timestamp;
u_int64_t packets = 0;
u_int64_t bytes = 0;
openlog("testobsdrdr", LOG_PERROR, LOG_USER);
if(init_redirect() < 0)
{
fprintf(stderr, "init_redirect() failed\n");
return 1;
}
//add_redirect_rule("ep0", 12123, "192.168.1.23", 1234);
//add_redirect_rule2("ep0", 12155, "192.168.1.155", 1255, IPPROTO_TCP);
add_redirect_rule2("ep0", "8.8.8.8", 12123, "192.168.1.125", 1234,
IPPROTO_UDP, "test description", 0);
//add_redirect_rule2("em0", 12123, "127.1.2.3", 1234,
// IPPROTO_TCP, "test description tcp");
list_rules();
list_eports_tcp();
if(get_redirect_rule("xl1", 4662, IPPROTO_TCP,
buf, sizeof(buf), &iport, desc, sizeof(desc),
&timestamp,
&packets, &bytes) < 0)
printf("get_redirect_rule() failed\n");
else
{
printf("\n%s:%d '%s' packets=%llu bytes=%llu\n", buf, (int)iport, desc,
packets, bytes);
}
#if 0
if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0)
printf("delete_redirect_rule() failed\n");
else
printf("delete_redirect_rule() succeded\n");
if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0)
printf("delete_redirect_rule() failed\n");
else
printf("delete_redirect_rule() succeded\n");
#endif
//test_index();
//clear_redirect_rules();
//list_rules();
return 0;
}

View File

@ -0,0 +1,100 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner and Thomas Bernard
* (c) 2007 Darren Reed
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <kstat.h>
#include <sys/sysmacros.h>
#include "../getifstats.h"
int
getifstats(const char * ifname, struct ifdata * data)
{
char buffer[64], *s;
kstat_named_t *kn;
kstat_ctl_t *kc;
int instance;
kstat_t *ksp;
uint32_t cnt32;
void *ptr;
if (data == NULL)
goto error;
if (ifname == NULL || *ifname == '\0')
goto error;
s = (char *)ifname + strlen(ifname);
s--;
while ((s > ifname) && isdigit(*s))
s--;
s++;
instance = atoi(s);
strlcpy(buffer, ifname, MIN(s - ifname + 1, 64));
kc = kstat_open();
if (kc != NULL) {
ksp = kstat_lookup(kc, buffer, instance, (char *)ifname);
if (ksp && (kstat_read(kc, ksp, NULL) != -1)) {
/* found the right interface */
if (sizeof(long) == 8) {
uint64_t cnt64;
kn = kstat_data_lookup(ksp, "rbytes64");
if (kn != NULL) {
data->ibytes = kn->value.i64;
}
kn = kstat_data_lookup(ksp, "ipackets64");
if (kn != NULL) {
data->ipackets = kn->value.i64;
}
kn = kstat_data_lookup(ksp, "obytes64");
if (kn != NULL) {
data->obytes = kn->value.i64;
}
kn = kstat_data_lookup(ksp, "opackets64");
if (kn != NULL) {
data->opackets = kn->value.i64;
}
} else {
kn = kstat_data_lookup(ksp, "rbytes");
if (kn != NULL) {
data->ibytes = kn->value.i32;
}
kn = kstat_data_lookup(ksp, "ipackets");
if (kn != NULL) {
data->ipackets = kn->value.i32;
}
kn = kstat_data_lookup(ksp, "obytes");
if (kn != NULL) {
data->obytes = kn->value.i32;
}
kn = kstat_data_lookup(ksp, "opackets");
if (kn != NULL) {
data->ipackets = kn->value.i32;
}
}
kn = kstat_data_lookup(ksp, "ifspeed");
if (kn != NULL) {
data->baudrate = kn->value.i32;
}
kstat_close(kc);
return 0; /* ok */
}
syslog(LOG_ERR, "kstat_lookup/read() failed: %m");
kstat_close(kc);
return -1;
} else {
syslog(LOG_ERR, "kstat_open() failed: %m");
}
error:
return -1; /* not found or error */
}

24
miniupnpd/testgetifaddr.c Normal file
View File

@ -0,0 +1,24 @@
/* $Id: testgetifaddr.c,v 1.3 2011/04/11 10:41:57 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <syslog.h>
#include "getifaddr.h"
int main(int argc, char * * argv) {
char addr[64];
if(argc < 2) {
fprintf(stderr, "Usage:\t%s interface_name\n", argv[0]);
return 1;
}
openlog("testgetifaddr", LOG_CONS|LOG_PERROR, LOG_USER);
if(getifaddr(argv[1], addr, sizeof(addr)) < 0) {
fprintf(stderr, "Cannot get address for interface %s.\n", argv[1]);
return 1;
}
printf("Interface %s has IP address %s.\n", argv[1], addr);
return 0;
}

View File

@ -0,0 +1,36 @@
/* $Id: testgetifstats.c,v 1.4 2007/02/07 22:14:47 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include "getifstats.h"
int
main(int argc, char **argv)
{
int r;
struct ifdata data;
if(argc<2)
{
fprintf(stderr, "usage : %s <ifname>\n", argv[0]);
return 1;
}
openlog("testgetifstats", LOG_CONS|LOG_PERROR, LOG_USER);
memset(&data, 0, sizeof(data));
r = getifstats(argv[1], &data);
printf("getifstats() returned %d\n", r);
printf("stats for interface %s :\n", argv[1]);
printf("bitrate = %lu\n", data.baudrate);
printf(" input packets : %9lu\t input bytes : %9lu\n",
data.ipackets, data.ibytes);
printf("output packets : %9lu\toutput bytes : %9lu\n",
data.opackets, data.obytes);
return 0;
}

228
miniupnpd/testupnpdescgen.c Normal file
View File

@ -0,0 +1,228 @@
/* $Id: testupnpdescgen.c,v 1.25 2011/05/18 22:22:23 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* for mkdir */
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include "config.h"
#include "upnpdescgen.h"
char uuidvalue[] = "uuid:12345678-0000-0000-0000-00000000abcd";
char serialnumber[] = "12345678";
char modelnumber[] = "1";
char presentationurl[] = "http://192.168.0.1:8080/";
/*char presentationurl[] = "";*/
char * use_ext_ip_addr = NULL;
const char * ext_if_name = "eth0";
#ifdef ENABLE_6FC_SERVICE
int ipv6fc_firewall_enabled = 1;
int ipv6fc_inbound_pinhole_allowed = 1;
#endif
int getifaddr(const char * ifname, char * buf, int len)
{
strncpy(buf, "1.2.3.4", len);
return 0;
}
int upnp_get_portmapping_number_of_entries(void)
{
return 42;
}
int get_wan_connection_status(const char * ifname)
{
return 2;
}
/* To be improved */
int
xml_pretty_print(const char * s, int len, FILE * f)
{
int n = 0, i;
int elt_close = 0;
int c, indent = 0;
if(!s)
return n;
while(len > 0)
{
c = *(s++); len--;
switch(c)
{
case '<':
if(len>0 && *s == '/')
elt_close++;
else if(len>0 && *s == '?')
elt_close = 1;
else
elt_close = 0;
if(elt_close!=1)
{
if(elt_close > 1)
indent--;
fputc('\n', f); n++;
for(i=indent; i>0; i--)
fputc(' ', f);
n += indent;
}
fputc(c, f); n++;
break;
case '>':
fputc(c, f); n++;
if(elt_close==1)
{
/*fputc('\n', f); n++; */
//elt_close = 0;
if(indent > 0)
indent--;
}
else if(elt_close == 0)
indent++;
break;
case '\n':
/* remove existing LF */
break;
default:
fputc(c, f); n++;
}
}
return n;
}
/* stupid test */
const char * str1 = "Prefix123String";
const char * str2 = "123String";
void stupid_test(void)
{
printf("str1:'%s' str2:'%s'\n", str1, str2);
printf("str1:%p str2:%p str2-str1:%ld\n", str1, str2, (long)(str2-str1));
}
/* main */
int
main(int argc, char * * argv)
{
char * rootDesc;
int rootDescLen;
char * s;
int l;
FILE * f;
if(mkdir("testdescs", 0777) < 0) {
if(errno != EEXIST) {
perror("mkdir");
}
}
printf("Root Description :\n");
rootDesc = genRootDesc(&rootDescLen);
xml_pretty_print(rootDesc, rootDescLen, stdout);
f = fopen("testdescs/rootdesc.xml", "w");
if(f) {
xml_pretty_print(rootDesc, rootDescLen, f);
fclose(f);
}
free(rootDesc);
printf("\n-------------\n");
printf("WANIPConnection Description :\n");
s = genWANIPCn(&l);
xml_pretty_print(s, l, stdout);
f = fopen("testdescs/wanipc_scpd.xml", "w");
if(f) {
xml_pretty_print(s, l, f);
fclose(f);
}
free(s);
printf("\n-------------\n");
printf("WANConfig Description :\n");
s = genWANCfg(&l);
xml_pretty_print(s, l, stdout);
f = fopen("testdescs/wanconfig_scpd.xml", "w");
if(f) {
xml_pretty_print(s, l, f);
fclose(f);
}
free(s);
printf("\n-------------\n");
#ifdef ENABLE_L3F_SERVICE
printf("Layer3Forwarding service :\n");
s = genL3F(&l);
xml_pretty_print(s, l, stdout);
f = fopen("testdescs/l3f_scpd.xml", "w");
if(f) {
xml_pretty_print(s, l, f);
fclose(f);
}
free(s);
printf("\n-------------\n");
#endif
#ifdef ENABLE_6FC_SERVICE
printf("WANIPv6FirewallControl service :\n");
s = gen6FC(&l);
xml_pretty_print(s, l, stdout);
f = fopen("testdescs/wanipv6fc_scpd.xml", "w");
if(f) {
xml_pretty_print(s, l, f);
fclose(f);
}
free(s);
printf("\n-------------\n");
#endif
#ifdef ENABLE_DP_SERVICE
printf("DeviceProtection service :\n");
s = genDP(&l);
xml_pretty_print(s, l, stdout);
f = fopen("testdescs/dp_scpd.xml", "w");
if(f) {
xml_pretty_print(s, l, f);
fclose(f);
}
free(s);
printf("\n-------------\n");
#endif
#ifdef ENABLE_EVENTS
s = getVarsWANIPCn(&l);
xml_pretty_print(s, l, stdout);
free(s);
printf("\n-------------\n");
s = getVarsWANCfg(&l);
xml_pretty_print(s, l, stdout);
free(s);
printf("\n-------------\n");
#ifdef ENABLE_L3F_SERVICE
s = getVarsL3F(&l);
xml_pretty_print(s, l, stdout);
free(s);
printf("\n-------------\n");
#ifdef ENABLE_6FC_SERVICE
s = getVars6FC(&l);
xml_pretty_print(s, l, stdout);
free(s);
printf("\n-------------\n");
#endif
#ifdef ENABLE_DP_SERVICE
s = getVarsDP(&l);
xml_pretty_print(s, l, stdout);
free(s);
printf("\n-------------\n");
#endif
#endif
#endif
/*
stupid_test();
*/
return 0;
}

View File

@ -0,0 +1,62 @@
/* $Id: testupnppermissions.c,v 1.3 2009/09/14 15:24:46 nanard Exp $ */
/* (c) 2007-2009 Thomas Bernard
* MiniUPnP Project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*/
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "upnppermissions.h"
void
print_upnpperm(const struct upnpperm * p)
{
switch(p->type)
{
case UPNPPERM_ALLOW:
printf("allow ");
break;
case UPNPPERM_DENY:
printf("deny ");
break;
default:
printf("error ! ");
}
printf("%hu-%hu ", p->eport_min, p->eport_max);
printf("%s/", inet_ntoa(p->address));
printf("%s ", inet_ntoa(p->mask));
printf("%hu-%hu", p->iport_min, p->iport_max);
putchar('\n');
}
int main(int argc, char * * argv)
{
int i, r;
struct upnpperm p;
if(argc < 2) {
fprintf(stderr, "Usage: %s \"permission line\" [...]\n", argv[0]);
fprintf(stderr, "Example: %s \"allow 1234 10.10.10.10/32 1234\"\n", argv[0]);
return 1;
}
openlog("testupnppermissions", LOG_PERROR, LOG_USER);
/* for(i=0; i<argc; i++)
printf("%2d '%s'\n", i, argv[i]); */
for(i=1; i<argc; i++) {
printf("%2d '%s'\n", i, argv[i]);
memset(&p, 0, sizeof(struct upnpperm));
r = read_permission_line(&p, argv[i]);
if(r==0) {
printf("Permission read successfully\n");
print_upnpperm(&p);
} else {
printf("Permission read failed, please check its correctness\n");
}
putchar('\n');
}
return 0;
}

1274
miniupnpd/upnpdescgen.c Normal file

File diff suppressed because it is too large Load Diff

102
miniupnpd/upnpdescgen.h Normal file
View File

@ -0,0 +1,102 @@
/* $Id: upnpdescgen.h,v 1.22 2011/05/18 22:22:24 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPDESCGEN_H__
#define __UPNPDESCGEN_H__
#include "config.h"
/* for the root description
* The child list reference is stored in "data" member using the
* INITHELPER macro with index/nchild always in the
* same order, whatever the endianness */
struct XMLElt {
const char * eltname; /* begin with '/' if no child */
const char * data; /* Value */
};
/* for service description */
struct serviceDesc {
const struct action * actionList;
const struct stateVar * serviceStateTable;
};
struct action {
const char * name;
const struct argument * args;
};
struct argument { /* the name of the arg is obtained from the variable */
unsigned char dir; /* MSB : don't append "New" Flag,
* 5 Medium bits : magic argument name index
* 2 LSB : 1 = in, 2 = out */
unsigned char relatedVar; /* index of the related variable */
};
struct stateVar {
const char * name;
unsigned char itype; /* MSB: sendEvent flag, 7 LSB: index in upnptypes */
unsigned char idefault; /* default value */
unsigned char iallowedlist; /* index in allowed values list
* or in allowed range list */
unsigned char ieventvalue; /* fixed value returned or magical values */
};
/* little endian
* The code has now be tested on big endian architecture */
#define INITHELPER(i, n) ((char *)(((n)<<16)|(i)))
/* char * genRootDesc(int *);
* returns: NULL on error, string allocated on the heap */
char *
genRootDesc(int * len);
/* for the two following functions */
char *
genWANIPCn(int * len);
char *
genWANCfg(int * len);
#ifdef ENABLE_L3F_SERVICE
char *
genL3F(int * len);
#endif
#ifdef ENABLE_6FC_SERVICE
char *
gen6FC(int * len);
#endif
#ifdef ENABLE_DP_SERVICE
char *
genDP(int * len);
#endif
#ifdef ENABLE_EVENTS
char *
getVarsWANIPCn(int * len);
char *
getVarsWANCfg(int * len);
#ifdef ENABLE_L3F_SERVICE
char *
getVarsL3F(int * len);
#endif
#ifdef ENABLE_6FC_SERVICE
char *
getVars6FC(int * len);
#endif
#ifdef ENABLE_DP_SERVICE
char *
getVarsDP(int * len);
#endif
#endif /* ENABLE_EVENTS */
#endif

View File

@ -0,0 +1,39 @@
/* $Id: upnpdescstrings.h,v 1.5 2007/02/09 10:12:52 nanard Exp $ */
/* miniupnp project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the coditions detailed in
* the LICENCE file provided within the distribution */
#ifndef __UPNPDESCSTRINGS_H__
#define __UPNPDESCSTRINGS_H__
#include "config.h"
/* strings used in the root device xml description */
#define ROOTDEV_FRIENDLYNAME OS_NAME " router"
#define ROOTDEV_MANUFACTURER OS_NAME
#define ROOTDEV_MANUFACTURERURL OS_URL
#define ROOTDEV_MODELNAME OS_NAME " router"
#define ROOTDEV_MODELDESCRIPTION OS_NAME " router"
#define ROOTDEV_MODELURL OS_URL
#define WANDEV_FRIENDLYNAME "WANDevice"
#define WANDEV_MANUFACTURER "MiniUPnP"
#define WANDEV_MANUFACTURERURL "http://miniupnp.free.fr/"
#define WANDEV_MODELNAME "WAN Device"
#define WANDEV_MODELDESCRIPTION "WAN Device"
#define WANDEV_MODELNUMBER UPNP_VERSION
#define WANDEV_MODELURL "http://miniupnp.free.fr/"
#define WANDEV_UPC "MINIUPNPD"
#define WANCDEV_FRIENDLYNAME "WANConnectionDevice"
#define WANCDEV_MANUFACTURER WANDEV_MANUFACTURER
#define WANCDEV_MANUFACTURERURL WANDEV_MANUFACTURERURL
#define WANCDEV_MODELNAME "MiniUPnPd"
#define WANCDEV_MODELDESCRIPTION "MiniUPnP daemon"
#define WANCDEV_MODELNUMBER UPNP_VERSION
#define WANCDEV_MODELURL "http://miniupnp.free.fr/"
#define WANCDEV_UPC "MINIUPNPD"
#endif

536
miniupnpd/upnpevents.c Normal file
View File

@ -0,0 +1,536 @@
/* $Id: upnpevents.c,v 1.17 2011/06/27 11:24:00 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2008-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include "config.h"
#include "upnpevents.h"
#include "miniupnpdpath.h"
#include "upnpglobalvars.h"
#include "upnpdescgen.h"
#ifdef ENABLE_EVENTS
/*enum subscriber_service_enum {
EWanCFG = 1,
EWanIPC,
EL3F
};*/
/* stuctures definitions */
struct subscriber {
LIST_ENTRY(subscriber) entries;
struct upnp_event_notify * notify;
time_t timeout;
uint32_t seq;
enum subscriber_service_enum service;
char uuid[42];
char callback[];
};
struct upnp_event_notify {
LIST_ENTRY(upnp_event_notify) entries;
int s; /* socket */
enum { ECreated=1,
EConnecting,
ESending,
EWaitingForResponse,
EFinished,
EError } state;
struct subscriber * sub;
char * buffer;
int buffersize;
int tosend;
int sent;
const char * path;
#ifdef ENABLE_IPV6
int ipv6;
char addrstr[48];
#else
char addrstr[16];
#endif
char portstr[8];
};
/* prototypes */
static void
upnp_event_create_notify(struct subscriber * sub);
/* Subscriber list */
LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
/* notify list */
LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
/* create a new subscriber */
static struct subscriber *
newSubscriber(const char * eventurl, const char * callback, int callbacklen)
{
struct subscriber * tmp;
if(!eventurl || !callback || !callbacklen)
return NULL;
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
if(!tmp)
return NULL;
if(strcmp(eventurl, WANCFG_EVENTURL)==0)
tmp->service = EWanCFG;
else if(strcmp(eventurl, WANIPC_EVENTURL)==0)
tmp->service = EWanIPC;
#ifdef ENABLE_L3F_SERVICE
else if(strcmp(eventurl, L3F_EVENTURL)==0)
tmp->service = EL3F;
#endif
else {
free(tmp);
return NULL;
}
memcpy(tmp->callback, callback, callbacklen);
tmp->callback[callbacklen] = '\0';
/* make a dummy uuid */
/* TODO: improve that */
strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
return tmp;
}
/* creates a new subscriber and adds it to the subscriber list
* also initiate 1st notify
* TODO : add a check on the number of subscriber in order to
* prevent memory overflow... */
const char *
upnpevents_addSubscriber(const char * eventurl,
const char * callback, int callbacklen,
int timeout)
{
struct subscriber * tmp;
/*static char uuid[42];*/
/* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */
syslog(LOG_DEBUG, "addSubscriber(%s, %.*s, %d)",
eventurl, callbacklen, callback, timeout);
/*strncpy(uuid, uuidvalue, sizeof(uuid));
uuid[sizeof(uuid)-1] = '\0';*/
tmp = newSubscriber(eventurl, callback, callbacklen);
if(!tmp)
return NULL;
if(timeout)
tmp->timeout = time(NULL) + timeout;
LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
upnp_event_create_notify(tmp);
return tmp->uuid;
}
/* renew a subscription (update the timeout) */
int
renewSubscription(const char * sid, int sidlen, int timeout)
{
struct subscriber * sub;
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
if(memcmp(sid, sub->uuid, 41) == 0) {
sub->timeout = (timeout ? time(NULL) + timeout : 0);
return 0;
}
}
return -1;
}
int
upnpevents_removeSubscriber(const char * sid, int sidlen)
{
struct subscriber * sub;
if(!sid)
return -1;
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
if(memcmp(sid, sub->uuid, 41) == 0) {
if(sub->notify) {
sub->notify->sub = NULL;
}
LIST_REMOVE(sub, entries);
free(sub);
return 0;
}
}
return -1;
}
/* notifies all subscriber of a number of port mapping change
* or external ip address change */
void
upnp_event_var_change_notify(enum subscriber_service_enum service)
{
struct subscriber * sub;
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
if(sub->service == service && sub->notify == NULL)
upnp_event_create_notify(sub);
}
}
/* create and add the notify object to the list */
static void
upnp_event_create_notify(struct subscriber * sub)
{
struct upnp_event_notify * obj;
int flags;
obj = calloc(1, sizeof(struct upnp_event_notify));
if(!obj) {
syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify");
return;
}
obj->sub = sub;
obj->state = ECreated;
#ifdef ENABLE_IPV6
obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET,
SOCK_STREAM, 0);
#else
obj->s = socket(PF_INET, SOCK_STREAM, 0);
#endif
if(obj->s<0) {
syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
goto error;
}
if((flags = fcntl(obj->s, F_GETFL, 0)) < 0) {
syslog(LOG_ERR, "%s: fcntl(..F_GETFL..): %m",
"upnp_event_create_notify");
goto error;
}
if(fcntl(obj->s, F_SETFL, flags | O_NONBLOCK) < 0) {
syslog(LOG_ERR, "%s: fcntl(..F_SETFL..): %m",
"upnp_event_create_notify");
goto error;
}
if(sub)
sub->notify = obj;
LIST_INSERT_HEAD(&notifylist, obj, entries);
return;
error:
if(obj->s >= 0)
close(obj->s);
free(obj);
}
static void
upnp_event_notify_connect(struct upnp_event_notify * obj)
{
int i;
const char * p;
unsigned short port;
#ifdef ENABLE_IPV6
struct sockaddr_storage addr;
#else
struct sockaddr_in addr;
#endif
if(!obj)
return;
memset(&addr, 0, sizeof(addr));
i = 0;
if(obj->sub == NULL) {
obj->state = EError;
return;
}
p = obj->sub->callback;
p += 7; /* http:// */
#ifdef ENABLE_IPV6
if(*p == '[') { /* ip v6 */
p++;
obj->ipv6 = 1;
while(*p != ']' && i < (sizeof(obj->addrstr)-1))
obj->addrstr[i++] = *(p++);
if(*p == ']')
p++;
} else {
#endif
while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1))
obj->addrstr[i++] = *(p++);
#ifdef ENABLE_IPV6
}
#endif
obj->addrstr[i] = '\0';
if(*p == ':') {
obj->portstr[0] = *p;
i = 1;
p++;
port = (unsigned short)atoi(p);
while(*p != '/') {
if(i<7) obj->portstr[i++] = *p;
p++;
}
obj->portstr[i] = 0;
} else {
port = 80;
obj->portstr[0] = '\0';
}
obj->path = p;
#ifdef ENABLE_IPV6
if(obj->ipv6) {
struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&addr;
sa->sin6_family = AF_INET6;
inet_pton(AF_INET6, obj->addrstr, &(sa->sin6_addr));
sa->sin6_port = htons(port);
} else {
struct sockaddr_in * sa = (struct sockaddr_in *)&addr;
sa->sin_family = AF_INET;
inet_pton(AF_INET, obj->addrstr, &(sa->sin_addr));
sa->sin_port = htons(port);
}
#else
addr.sin_family = AF_INET;
inet_aton(obj->addrstr, &addr.sin_addr);
addr.sin_port = htons(port);
#endif
syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect",
obj->addrstr, port, obj->path);
obj->state = EConnecting;
if(connect(obj->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if(errno != EINPROGRESS && errno != EWOULDBLOCK) {
syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect");
obj->state = EError;
}
}
}
static void upnp_event_prepare(struct upnp_event_notify * obj)
{
static const char notifymsg[] =
"NOTIFY %s HTTP/1.1\r\n"
"Host: %s%s\r\n"
"Content-Type: text/xml\r\n"
"Content-Length: %d\r\n"
"NT: upnp:event\r\n"
"NTS: upnp:propchange\r\n"
"SID: %s\r\n"
"SEQ: %u\r\n"
"Connection: close\r\n"
"Cache-Control: no-cache\r\n"
"\r\n"
"%.*s\r\n";
char * xml;
int l;
if(obj->sub == NULL) {
obj->state = EError;
return;
}
switch(obj->sub->service) {
case EWanCFG:
xml = getVarsWANCfg(&l);
break;
case EWanIPC:
xml = getVarsWANIPCn(&l);
break;
#ifdef ENABLE_L3F_SERVICE
case EL3F:
xml = getVarsL3F(&l);
break;
#endif
#ifdef ENABLE_6FC_SERVICE
case E6FC:
xml = getVars6FC(&l);
break;
#endif
#ifdef ENABLE_DP_SERVICE
case EDP:
xml = getVarsDP(&l);
break;
#endif
default:
xml = NULL;
l = 0;
}
obj->buffersize = 1024;
obj->buffer = malloc(obj->buffersize);
/*if(!obj->buffer) {
}*/
obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg,
obj->path, obj->addrstr, obj->portstr, l+2,
obj->sub->uuid, obj->sub->seq,
l, xml);
if(xml) {
free(xml);
xml = NULL;
}
obj->state = ESending;
}
static void upnp_event_send(struct upnp_event_notify * obj)
{
int i;
syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s",
"upnp_event_send", obj->addrstr, obj->portstr);
syslog(LOG_DEBUG, "%s: msg: %s",
"upnp_event_send", obj->buffer + obj->sent);
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
if(i<0) {
syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
obj->state = EError;
return;
}
else if(i != (obj->tosend - obj->sent))
syslog(LOG_NOTICE, "%s: %d bytes send out of %d",
"upnp_event_send", i, obj->tosend - obj->sent);
obj->sent += i;
if(obj->sent == obj->tosend)
obj->state = EWaitingForResponse;
}
static void upnp_event_recv(struct upnp_event_notify * obj)
{
int n;
n = recv(obj->s, obj->buffer, obj->buffersize, 0);
if(n<0) {
syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv");
obj->state = EError;
return;
}
syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
n, n, obj->buffer);
obj->state = EFinished;
if(obj->sub)
obj->sub->seq++;
}
static void
upnp_event_process_notify(struct upnp_event_notify * obj)
{
switch(obj->state) {
case EConnecting:
/* now connected or failed to connect */
upnp_event_prepare(obj);
upnp_event_send(obj);
break;
case ESending:
upnp_event_send(obj);
break;
case EWaitingForResponse:
upnp_event_recv(obj);
break;
case EFinished:
close(obj->s);
obj->s = -1;
break;
default:
syslog(LOG_ERR, "upnp_event_process_notify: unknown state");
}
}
void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd)
{
struct upnp_event_notify * obj;
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d",
obj, obj->state, obj->s);
if(obj->s >= 0) {
switch(obj->state) {
case ECreated:
upnp_event_notify_connect(obj);
if(obj->state != EConnecting)
break;
case EConnecting:
case ESending:
FD_SET(obj->s, writeset);
if(obj->s > *max_fd)
*max_fd = obj->s;
break;
case EWaitingForResponse:
FD_SET(obj->s, readset);
if(obj->s > *max_fd)
*max_fd = obj->s;
break;
default:
;
}
}
}
}
void upnpevents_processfds(fd_set *readset, fd_set *writeset)
{
struct upnp_event_notify * obj;
struct upnp_event_notify * next;
struct subscriber * sub;
struct subscriber * subnext;
time_t curtime;
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
syslog(LOG_DEBUG, "%s: %p %d %d %d %d",
"upnpevents_processfds", obj, obj->state, obj->s,
FD_ISSET(obj->s, readset), FD_ISSET(obj->s, writeset));
if(obj->s >= 0) {
if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset))
upnp_event_process_notify(obj);
}
}
obj = notifylist.lh_first;
while(obj != NULL) {
next = obj->entries.le_next;
if(obj->state == EError || obj->state == EFinished) {
if(obj->s >= 0) {
close(obj->s);
}
if(obj->sub)
obj->sub->notify = NULL;
/* remove also the subscriber from the list if there was an error */
if(obj->state == EError && obj->sub) {
LIST_REMOVE(obj->sub, entries);
free(obj->sub);
}
if(obj->buffer) {
free(obj->buffer);
}
LIST_REMOVE(obj, entries);
free(obj);
}
obj = next;
}
/* remove timeouted subscribers */
curtime = time(NULL);
for(sub = subscriberlist.lh_first; sub != NULL; ) {
subnext = sub->entries.le_next;
if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
LIST_REMOVE(sub, entries);
free(sub);
}
sub = subnext;
}
}
#ifdef USE_MINIUPNPDCTL
void write_events_details(int s) {
int n;
char buff[80];
struct upnp_event_notify * obj;
struct subscriber * sub;
write(s, "Events details :\n", 17);
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n",
obj, obj->sub, obj->state, obj->s);
write(s, buff, n);
}
write(s, "Subscribers :\n", 14);
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n",
sub, (int)sub->timeout, sub->seq, sub->service);
write(s, buff, n);
n = snprintf(buff, sizeof(buff), " notify=%p %s\n",
sub->notify, sub->uuid);
write(s, buff, n);
n = snprintf(buff, sizeof(buff), " %s\n",
sub->callback);
write(s, buff, n);
}
}
#endif
#endif

50
miniupnpd/upnpevents.h Normal file
View File

@ -0,0 +1,50 @@
/* $Id: upnpevents.h,v 1.9 2011/05/18 22:21:19 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2008-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPEVENTS_H__
#define __UPNPEVENTS_H__
#include "config.h"
#ifdef ENABLE_EVENTS
enum subscriber_service_enum {
EWanCFG = 1,
EWanIPC,
#ifdef ENABLE_L3F_SERVICE
EL3F,
#endif
#ifdef ENABLE_6FC_SERVICE
E6FC,
#endif
#ifdef ENABLE_DP_SERVICE
EDP,
#endif
};
void
upnp_event_var_change_notify(enum subscriber_service_enum service);
const char *
upnpevents_addSubscriber(const char * eventurl,
const char * callback, int callbacklen,
int timeout);
int
upnpevents_removeSubscriber(const char * sid, int sidlen);
int
renewSubscription(const char * sid, int sidlen, int timeout);
void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd);
void upnpevents_processfds(fd_set *readset, fd_set *writeset);
#ifdef USE_MINIUPNPDCTL
void write_events_details(int s);
#endif
#endif
#endif

View File

@ -0,0 +1,98 @@
/* $Id: upnpglobalvars.c,v 1.25 2011/05/27 21:36:22 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2010 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <sys/types.h>
#include <netinet/in.h>
#include "config.h"
#include "upnpglobalvars.h"
/* network interface for internet */
const char * ext_if_name = 0;
/* file to store leases */
#ifdef ENABLE_LEASEFILE
const char* lease_file = 0;
#endif
/* forced ip address to use for this interface
* when NULL, getifaddr() is used */
const char * use_ext_ip_addr = 0;
/* LAN address */
/*const char * listen_addr = 0;*/
unsigned long downstream_bitrate = 0;
unsigned long upstream_bitrate = 0;
/* startup time */
time_t startup_time = 0;
int runtime_flags = 0;
const char * pidfilename = "/var/run/miniupnpd.pid";
char uuidvalue[] = "uuid:00000000-0000-0000-0000-000000000000";
char serialnumber[SERIALNUMBER_MAX_LEN] = "00000000";
char modelnumber[MODELNUMBER_MAX_LEN] = "1";
/* presentation url :
* http://nnn.nnn.nnn.nnn:ppppp/ => max 30 bytes including terminating 0 */
char presentationurl[PRESENTATIONURL_MAX_LEN];
/* UPnP permission rules : */
struct upnpperm * upnppermlist = 0;
unsigned int num_upnpperm = 0;
#ifdef ENABLE_NATPMP
/* NAT-PMP */
#if 0
unsigned int nextnatpmptoclean_timestamp = 0;
unsigned short nextnatpmptoclean_eport = 0;
unsigned short nextnatpmptoclean_proto = 0;
#endif
#endif
/* For automatic removal of expired rules (with LeaseDuration) */
unsigned int nextruletoclean_timestamp = 0;
#ifdef USE_PF
const char * queue = 0;
const char * tag = 0;
#endif
#ifdef USE_NETFILTER
/* chain name to use, both in the nat table
* and the filter table */
const char * miniupnpd_nat_chain = "MINIUPNPD";
const char * miniupnpd_forward_chain = "MINIUPNPD";
#endif
#ifdef ENABLE_NFQUEUE
int nfqueue = -1;
int n_nfqix = 0;
unsigned nfqix[MAX_LAN_ADDR];
#endif
struct lan_addr_list lan_addrs;
#ifdef ENABLE_IPV6
/* ipv6 address used for HTTP */
char ipv6_addr_for_http_with_brackets[64];
#endif
/* Path of the Unix socket used to communicate with MiniSSDPd */
const char * minissdpdsocketpath = "/var/run/minissdpd.sock";
/* BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG */
unsigned int upnp_bootid = 1;
unsigned int upnp_configid = 1337;
#ifdef ENABLE_6FC_SERVICE
int ipv6fc_firewall_enabled = 1;
int ipv6fc_inbound_pinhole_allowed = 1;
#endif

121
miniupnpd/upnpglobalvars.h Normal file
View File

@ -0,0 +1,121 @@
/* $Id: upnpglobalvars.h,v 1.29 2011/05/27 21:36:22 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPGLOBALVARS_H__
#define __UPNPGLOBALVARS_H__
#include <time.h>
#include "upnppermissions.h"
#include "miniupnpdtypes.h"
#include "config.h"
/* name of the network interface used to acces internet */
extern const char * ext_if_name;
/* file to store all leases */
#ifdef ENABLE_LEASEFILE
extern const char * lease_file;
#endif
/* forced ip address to use for this interface
* when NULL, getifaddr() is used */
extern const char * use_ext_ip_addr;
/* parameters to return to upnp client when asked */
extern unsigned long downstream_bitrate;
extern unsigned long upstream_bitrate;
/* statup time */
extern time_t startup_time;
/* runtime boolean flags */
extern int runtime_flags;
#define LOGPACKETSMASK 0x0001
#define SYSUPTIMEMASK 0x0002
#ifdef ENABLE_NATPMP
#define ENABLENATPMPMASK 0x0004
#endif
#define CHECKCLIENTIPMASK 0x0008
#define SECUREMODEMASK 0x0010
#define ENABLEUPNPMASK 0x0020
#ifdef PF_ENABLE_FILTER_RULES
#define PFNOQUICKRULESMASK 0x0040
#endif
#define SETFLAG(mask) runtime_flags |= mask
#define GETFLAG(mask) (runtime_flags & mask)
#define CLEARFLAG(mask) runtime_flags &= ~mask
extern const char * pidfilename;
extern char uuidvalue[];
#define SERIALNUMBER_MAX_LEN (10)
extern char serialnumber[];
#define MODELNUMBER_MAX_LEN (48)
extern char modelnumber[];
#define PRESENTATIONURL_MAX_LEN (64)
extern char presentationurl[];
/* UPnP permission rules : */
extern struct upnpperm * upnppermlist;
extern unsigned int num_upnpperm;
#ifdef ENABLE_NATPMP
/* NAT-PMP */
#if 0
extern unsigned int nextnatpmptoclean_timestamp;
extern unsigned short nextnatpmptoclean_eport;
extern unsigned short nextnatpmptoclean_proto;
#endif
#endif
/* For automatic removal of expired rules (with LeaseDuration) */
extern unsigned int nextruletoclean_timestamp;
#ifdef USE_PF
/* queue and tag for PF rules */
extern const char * queue;
extern const char * tag;
#endif
#ifdef USE_NETFILTER
extern const char * miniupnpd_nat_chain;
extern const char * miniupnpd_forward_chain;
#endif
#ifdef ENABLE_NFQUEUE
extern int nfqueue;
extern int n_nfqix;
extern unsigned nfqix[];
#endif
/* lan addresses to listen to SSDP traffic */
extern struct lan_addr_list lan_addrs;
#ifdef ENABLE_IPV6
/* ipv6 address used for HTTP */
extern char ipv6_addr_for_http_with_brackets[64];
#endif
extern const char * minissdpdsocketpath;
/* BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG */
extern unsigned int upnp_bootid;
extern unsigned int upnp_configid;
#ifdef ENABLE_6FC_SERVICE
extern int ipv6fc_firewall_enabled;
extern int ipv6fc_inbound_pinhole_allowed;
#endif
#endif

747
miniupnpd/upnphttp.c Normal file
View File

@ -0,0 +1,747 @@
/* $Id: upnphttp.c,v 1.62 2011/08/26 12:46:14 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* Copyright (c) 2005-2011 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file included in this distribution.
* */
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <ctype.h>
#include "config.h"
#include "upnphttp.h"
#include "upnpdescgen.h"
#include "miniupnpdpath.h"
#include "upnpsoap.h"
#include "upnpevents.h"
struct upnphttp *
New_upnphttp(int s)
{
struct upnphttp * ret;
if(s<0)
return NULL;
ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
if(ret == NULL)
return NULL;
memset(ret, 0, sizeof(struct upnphttp));
ret->socket = s;
return ret;
}
void
CloseSocket_upnphttp(struct upnphttp * h)
{
if(close(h->socket) < 0)
{
syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
}
h->socket = -1;
h->state = 100;
}
void
Delete_upnphttp(struct upnphttp * h)
{
if(h)
{
if(h->socket >= 0)
CloseSocket_upnphttp(h);
if(h->req_buf)
free(h->req_buf);
if(h->res_buf)
free(h->res_buf);
free(h);
}
}
/* parse HttpHeaders of the REQUEST */
static void
ParseHttpHeaders(struct upnphttp * h)
{
char * line;
char * colon;
char * p;
int n;
line = h->req_buf;
/* TODO : check if req_buf, contentoff are ok */
while(line < (h->req_buf + h->req_contentoff))
{
colon = strchr(line, ':');
if(colon)
{
if(strncasecmp(line, "Content-Length", 14)==0)
{
p = colon;
while(*p < '0' || *p > '9')
p++;
h->req_contentlen = atoi(p);
/*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
printf(" readbufflen=%d contentoff = %d\n",
h->req_buflen, h->req_contentoff);*/
}
else if(strncasecmp(line, "SOAPAction", 10)==0)
{
p = colon;
n = 0;
while(*p == ':' || *p == ' ' || *p == '\t')
p++;
while(p[n]>=' ')
{
n++;
}
if((p[0] == '"' && p[n-1] == '"')
|| (p[0] == '\'' && p[n-1] == '\''))
{
p++; n -= 2;
}
h->req_soapAction = p;
h->req_soapActionLen = n;
}
#ifdef ENABLE_EVENTS
else if(strncasecmp(line, "Callback", 8)==0)
{
p = colon;
while(*p != '<' && *p != '\r' )
p++;
n = 0;
while(p[n] != '>' && p[n] != '\r' )
n++;
h->req_Callback = p + 1;
h->req_CallbackLen = MAX(0, n - 1);
}
else if(strncasecmp(line, "SID", 3)==0)
{
p = colon + 1;
while(isspace(*p))
p++;
n = 0;
while(!isspace(p[n]))
n++;
h->req_SID = p;
h->req_SIDLen = n;
}
/* Timeout: Seconds-nnnn */
/* TIMEOUT
Recommended. Requested duration until subscription expires,
either number of seconds or infinite. Recommendation
by a UPnP Forum working committee. Defined by UPnP vendor.
Consists of the keyword "Second-" followed (without an
intervening space) by either an integer or the keyword "infinite". */
else if(strncasecmp(line, "Timeout", 7)==0)
{
p = colon + 1;
while(isspace(*p))
p++;
if(strncasecmp(p, "Second-", 7)==0) {
h->req_Timeout = atoi(p+7);
}
}
#endif
}
while(!(line[0] == '\r' && line[1] == '\n'))
line++;
line += 2;
}
}
/* very minimalistic 404 error message */
static void
Send404(struct upnphttp * h)
{
/*
static const char error404[] = "HTTP/1.1 404 Not found\r\n"
"Connection: close\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
"<BODY><H1>Not Found</H1>The requested URL was not found"
" on this server.</BODY></HTML>\r\n";
int n;
n = send(h->socket, error404, sizeof(error404) - 1, 0);
if(n < 0)
{
syslog(LOG_ERR, "Send404: send(http): %m");
}*/
static const char body404[] =
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
"<BODY><H1>Not Found</H1>The requested URL was not found"
" on this server.</BODY></HTML>\r\n";
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 404, "Not Found",
body404, sizeof(body404) - 1);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
/* very minimalistic 501 error message */
static void
Send501(struct upnphttp * h)
{
/*
static const char error501[] = "HTTP/1.1 501 Not Implemented\r\n"
"Connection: close\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
"<BODY><H1>Not Implemented</H1>The HTTP Method "
"is not implemented by this server.</BODY></HTML>\r\n";
int n;
n = send(h->socket, error501, sizeof(error501) - 1, 0);
if(n < 0)
{
syslog(LOG_ERR, "Send501: send(http): %m");
}
*/
static const char body501[] =
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
"<BODY><H1>Not Implemented</H1>The HTTP Method "
"is not implemented by this server.</BODY></HTML>\r\n";
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 501, "Not Implemented",
body501, sizeof(body501) - 1);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
static const char *
findendheaders(const char * s, int len)
{
while(len-->0)
{
if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
return s;
s++;
}
return NULL;
}
#ifdef HAS_DUMMY_SERVICE
static void
sendDummyDesc(struct upnphttp * h)
{
static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
" <specVersion>"
" <major>1</major>"
" <minor>0</minor>"
" </specVersion>"
" <actionList />"
" <serviceStateTable />"
"</scpd>\r\n";
BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
#endif
/* Sends the description generated by the parameter */
static void
sendXMLdesc(struct upnphttp * h, char * (f)(int *))
{
char * desc;
int len;
desc = f(&len);
if(!desc)
{
static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
"</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
syslog(LOG_ERR, "Failed to generate XML description");
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 500, "Internal Server Error",
error500, sizeof(error500)-1);
}
else
{
BuildResp_upnphttp(h, desc, len);
}
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
free(desc);
}
/* ProcessHTTPPOST_upnphttp()
* executes the SOAP query if it is possible */
static void
ProcessHTTPPOST_upnphttp(struct upnphttp * h)
{
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
{
if(h->req_soapAction)
{
/* we can process the request */
syslog(LOG_INFO, "SOAPAction: %.*s",
h->req_soapActionLen, h->req_soapAction);
ExecuteSoapAction(h,
h->req_soapAction,
h->req_soapActionLen);
}
else
{
static const char err400str[] =
"<html><body>Bad request</body></html>";
syslog(LOG_INFO, "No SOAPAction in HTTP headers");
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 400, "Bad Request",
err400str, sizeof(err400str) - 1);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
}
else
{
/* waiting for remaining data */
h->state = 1;
}
}
#ifdef ENABLE_EVENTS
/**
* returns 0 if the callback header value is not valid
* 1 if it is valid.
*/
static int
checkCallbackURL(struct upnphttp * h)
{
char addrstr[48];
int ipv6;
const char * p;
int i;
if(!h->req_Callback || h->req_CallbackLen < 8)
return 0;
if(memcmp(h->req_Callback, "http://", 7) != 0)
return 0;
ipv6 = 0;
i = 0;
p = h->req_Callback + 7;
if(*p == '[') {
p++;
ipv6 = 1;
while(*p != ']' && i < (sizeof(addrstr)-1)
&& p < (h->req_Callback + h->req_CallbackLen))
addrstr[i++] = *(p++);
} else {
while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
&& p < (h->req_Callback + h->req_CallbackLen))
addrstr[i++] = *(p++);
}
addrstr[i] = '\0';
if(ipv6) {
struct in6_addr addr;
if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
return 0;
#ifdef ENABLE_IPV6
if(!h->ipv6
|| (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
return 0;
#else
return 0;
#endif
} else {
struct in_addr addr;
if(inet_pton(AF_INET, addrstr, &addr) <= 0)
return 0;
#ifdef ENABLE_IPV6
if(h->ipv6) {
if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
return 0;
if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
return 0;
} else {
if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
return 0;
}
#else
if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
return 0;
#endif
}
return 1;
}
static void
ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
{
const char * sid;
syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
h->req_CallbackLen, h->req_Callback, h->req_Timeout);
syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
if(!h->req_Callback && !h->req_SID) {
/* Missing or invalid CALLBACK : 412 Precondition Failed.
* If CALLBACK header is missing or does not contain a valid HTTP URL,
* the publisher must respond with HTTP error 412 Precondition Failed*/
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
} else {
/* - add to the subscriber list
* - respond HTTP/x.x 200 OK
* - Send the initial event message */
/* Server:, SID:; Timeout: Second-(xx|infinite) */
/* Check that the callback URL is on the same IP as
* the request, and not on the internet, nor on ourself (DOS attack ?) */
if(h->req_Callback) {
if(checkCallbackURL(h)) {
sid = upnpevents_addSubscriber(path, h->req_Callback,
h->req_CallbackLen, h->req_Timeout);
h->respflags = FLAG_TIMEOUT;
if(sid) {
syslog(LOG_DEBUG, "generated sid=%s", sid);
h->respflags |= FLAG_SID;
h->req_SID = sid;
h->req_SIDLen = strlen(sid);
}
BuildResp_upnphttp(h, 0, 0);
} else {
syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
h->req_CallbackLen, h->req_Callback);
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
}
} else {
/* subscription renew */
/* Invalid SID
412 Precondition Failed. If a SID does not correspond to a known,
un-expired subscription, the publisher must respond
with HTTP error 412 Precondition Failed. */
if(renewSubscription(h->req_SID, h->req_SIDLen, h->req_Timeout) < 0) {
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
} else {
h->respflags = FLAG_TIMEOUT;
BuildResp_upnphttp(h, 0, 0);
}
}
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
}
static void
ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
{
syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
/* Remove from the list */
if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0) {
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
} else {
BuildResp_upnphttp(h, 0, 0);
}
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
#endif
/* Parse and process Http Query
* called once all the HTTP headers have been received. */
static void
ProcessHttpQuery_upnphttp(struct upnphttp * h)
{
char HttpCommand[16];
char HttpUrl[128];
char * HttpVer;
char * p;
int i;
p = h->req_buf;
if(!p)
return;
for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
HttpCommand[i] = *(p++);
HttpCommand[i] = '\0';
while(*p==' ')
p++;
for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
HttpUrl[i] = *(p++);
HttpUrl[i] = '\0';
while(*p==' ')
p++;
HttpVer = h->HttpVer;
for(i = 0; i<15 && *p != '\r'; i++)
HttpVer[i] = *(p++);
HttpVer[i] = '\0';
syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
HttpCommand, HttpUrl, HttpVer);
ParseHttpHeaders(h);
if(strcmp("POST", HttpCommand) == 0)
{
h->req_command = EPost;
ProcessHTTPPOST_upnphttp(h);
}
else if(strcmp("GET", HttpCommand) == 0)
{
h->req_command = EGet;
if(strcasecmp(ROOTDESC_PATH, HttpUrl) == 0)
{
sendXMLdesc(h, genRootDesc);
}
else if(strcasecmp(WANIPC_PATH, HttpUrl) == 0)
{
sendXMLdesc(h, genWANIPCn);
}
else if(strcasecmp(WANCFG_PATH, HttpUrl) == 0)
{
sendXMLdesc(h, genWANCfg);
}
#ifdef HAS_DUMMY_SERVICE
else if(strcasecmp(DUMMY_PATH, HttpUrl) == 0)
{
sendDummyDesc(h);
}
#endif
#ifdef ENABLE_L3F_SERVICE
else if(strcasecmp(L3F_PATH, HttpUrl) == 0)
{
sendXMLdesc(h, genL3F);
}
#endif
#ifdef ENABLE_6FC_SERVICE
else if(strcasecmp(WANIP6FC_PATH, HttpUrl) == 0)
{
sendXMLdesc(h, gen6FC);
}
#endif
#ifdef ENABLE_DP_SERVICE
else if(strcasecmp(DP_PATH, HttpUrl) == 0)
{
sendXMLdesc(h, genDP);
}
#endif
else
{
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
Send404(h);
}
}
#ifdef ENABLE_EVENTS
else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
{
h->req_command = ESubscribe;
ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
}
else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
{
h->req_command = EUnSubscribe;
ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
}
#else
else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
{
syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
Send501(h);
}
#endif
else
{
syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
Send501(h);
}
}
void
Process_upnphttp(struct upnphttp * h)
{
char buf[2048];
int n;
if(!h)
return;
switch(h->state)
{
case 0:
n = recv(h->socket, buf, 2048, 0);
if(n<0)
{
syslog(LOG_ERR, "recv (state0): %m");
h->state = 100;
}
else if(n==0)
{
syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
h->state = 100;
}
else
{
const char * endheaders;
/* if 1st arg of realloc() is null,
* realloc behaves the same as malloc() */
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
memcpy(h->req_buf + h->req_buflen, buf, n);
h->req_buflen += n;
h->req_buf[h->req_buflen] = '\0';
/* search for the string "\r\n\r\n" */
endheaders = findendheaders(h->req_buf, h->req_buflen);
if(endheaders)
{
h->req_contentoff = endheaders - h->req_buf + 4;
ProcessHttpQuery_upnphttp(h);
}
}
break;
case 1:
n = recv(h->socket, buf, 2048, 0);
if(n<0)
{
syslog(LOG_ERR, "recv (state1): %m");
h->state = 100;
}
else if(n==0)
{
syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
h->state = 100;
}
else
{
/*fwrite(buf, 1, n, stdout);*/ /* debug */
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
memcpy(h->req_buf + h->req_buflen, buf, n);
h->req_buflen += n;
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
{
ProcessHTTPPOST_upnphttp(h);
}
}
break;
default:
syslog(LOG_WARNING, "Unexpected state: %d", h->state);
}
}
static const char httpresphead[] =
"%s %d %s\r\n"
/*"Content-Type: text/xml; charset=\"utf-8\"\r\n"*/
"Content-Type: %s\r\n"
"Connection: close\r\n"
"Content-Length: %d\r\n"
"Server: " MINIUPNPD_SERVER_STRING "\r\n"
; /*"\r\n";*/
/*
"<?xml version=\"1.0\"?>\n"
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
"<s:Body>"
"</s:Body>"
"</s:Envelope>";
*/
/* with response code and response message
* also allocate enough memory */
void
BuildHeader_upnphttp(struct upnphttp * h, int respcode,
const char * respmsg,
int bodylen)
{
int templen;
if(!h->res_buf)
{
templen = sizeof(httpresphead) + 128 + bodylen;
h->res_buf = (char *)malloc(templen);
if(!h->res_buf)
{
syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
return;
}
h->res_buf_alloclen = templen;
}
h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
httpresphead, h->HttpVer,
respcode, respmsg,
(h->respflags&FLAG_HTML)?"text/html":"text/xml",
bodylen);
/* Additional headers */
#ifdef ENABLE_EVENTS
if(h->respflags & FLAG_TIMEOUT) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
"Timeout: Second-");
if(h->req_Timeout) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
"%d\r\n", h->req_Timeout);
} else {
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
"infinite\r\n");
}
}
if(h->respflags & FLAG_SID) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
"SID: %s\r\n", h->req_SID);
}
#endif
h->res_buf[h->res_buflen++] = '\r';
h->res_buf[h->res_buflen++] = '\n';
if(h->res_buf_alloclen < (h->res_buflen + bodylen))
{
char * tmp;
tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
if(tmp)
{
h->res_buf = tmp;
h->res_buf_alloclen = h->res_buflen + bodylen;
}
else
{
syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
}
}
}
void
BuildResp2_upnphttp(struct upnphttp * h, int respcode,
const char * respmsg,
const char * body, int bodylen)
{
BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
if(body)
memcpy(h->res_buf + h->res_buflen, body, bodylen);
h->res_buflen += bodylen;
}
/* responding 200 OK ! */
void
BuildResp_upnphttp(struct upnphttp * h,
const char * body, int bodylen)
{
BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
}
void
SendResp_upnphttp(struct upnphttp * h)
{
char * p;
ssize_t n;
size_t len;
p = h->res_buf;
len = h->res_buflen;
while (len > 0)
{
n = send(h->socket, p, len, 0);
if(n<0)
{
syslog(LOG_ERR, "send(res_buf): %m");
}
else if(n == 0)
{
syslog(LOG_ERR, "send(res_buf): %zd bytes sent (out of %zu)",
n, len);
break;
}
else
{
p += n;
len -= n;
}
}
}

118
miniupnpd/upnphttp.h Normal file
View File

@ -0,0 +1,118 @@
/* $Id: upnphttp.h,v 1.24 2011/06/27 11:06:00 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPHTTP_H__
#define __UPNPHTTP_H__
#include <netinet/in.h>
#include <sys/queue.h>
#include "config.h"
/* server: HTTP header returned in all HTTP responses : */
#define MINIUPNPD_SERVER_STRING OS_VERSION " UPnP/1.0 MiniUPnPd/" MINIUPNPD_VERSION
/*
states :
0 - waiting for data to read
1 - waiting for HTTP Post Content.
...
>= 100 - to be deleted
*/
enum httpCommands {
EUnknown = 0,
EGet,
EPost,
ESubscribe,
EUnSubscribe
};
struct upnphttp {
int socket;
struct in_addr clientaddr; /* client address */
#ifdef ENABLE_IPV6
int ipv6;
struct in6_addr clientaddr_v6;
#endif
int state;
char HttpVer[16];
/* request */
char * req_buf;
int req_buflen;
int req_contentlen;
int req_contentoff; /* header length */
enum httpCommands req_command;
const char * req_soapAction;
int req_soapActionLen;
#ifdef ENABLE_EVENTS
const char * req_Callback; /* For SUBSCRIBE */
int req_CallbackLen;
int req_Timeout;
const char * req_SID; /* For UNSUBSCRIBE */
int req_SIDLen;
#endif
int respflags; /* see FLAG_* constants below */
/* response */
char * res_buf;
int res_buflen;
int res_buf_alloclen;
/*int res_contentlen;*/
/*int res_contentoff;*/ /* header length */
LIST_ENTRY(upnphttp) entries;
};
/* Include the "Timeout:" header in response */
#define FLAG_TIMEOUT 0x01
/* Include the "SID:" header in response */
#define FLAG_SID 0x02
/* If set, the Content-Type is set to text/xml, otherwise it is text/xml */
#define FLAG_HTML 0x80
/* New_upnphttp() */
struct upnphttp *
New_upnphttp(int);
/* CloseSocket_upnphttp() */
void
CloseSocket_upnphttp(struct upnphttp *);
/* Delete_upnphttp() */
void
Delete_upnphttp(struct upnphttp *);
/* Process_upnphttp() */
void
Process_upnphttp(struct upnphttp *);
/* BuildHeader_upnphttp()
* build the header for the HTTP Response
* also allocate the buffer for body data */
void
BuildHeader_upnphttp(struct upnphttp * h, int respcode,
const char * respmsg,
int bodylen);
/* BuildResp_upnphttp()
* fill the res_buf buffer with the complete
* HTTP 200 OK response from the body passed as argument */
void
BuildResp_upnphttp(struct upnphttp *, const char *, int);
/* BuildResp2_upnphttp()
* same but with given response code/message */
void
BuildResp2_upnphttp(struct upnphttp * h, int respcode,
const char * respmsg,
const char * body, int bodylen);
/* SendResp_upnphttp() */
void
SendResp_upnphttp(struct upnphttp *);
#endif

189
miniupnpd/upnppermissions.c Normal file
View File

@ -0,0 +1,189 @@
/* $Id: upnppermissions.c,v 1.14 2009/12/22 17:21:43 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "config.h"
#include "upnppermissions.h"
int
read_permission_line(struct upnpperm * perm,
char * p)
{
char * q;
int n_bits;
/* first token: (allow|deny) */
while(isspace(*p))
p++;
if(0 == memcmp(p, "allow", 5))
{
perm->type = UPNPPERM_ALLOW;
p += 5;
}
else if(0 == memcmp(p, "deny", 4))
{
perm->type = UPNPPERM_DENY;
p += 4;
}
else
{
return -1;
}
/* second token: eport or eport_min-eport_max */
while(isspace(*p))
p++;
if(!isdigit(*p))
return -1;
for(q = p; isdigit(*q); q++);
if(*q=='-')
{
*q = '\0';
perm->eport_min = (u_short)atoi(p);
q++;
p = q;
while(isdigit(*q))
q++;
*q = '\0';
perm->eport_max = (u_short)atoi(p);
}
else
{
*q = '\0';
perm->eport_min = perm->eport_max = (u_short)atoi(p);
}
p = q + 1;
while(isspace(*p))
p++;
/* third token: ip/mask */
if(!isdigit(*p))
return -1;
for(q = p; isdigit(*q) || (*q == '.'); q++);
if(*q=='/')
{
*q = '\0';
if(!inet_aton(p, &perm->address))
return -1;
q++;
p = q;
while(isdigit(*q))
q++;
*q = '\0';
n_bits = atoi(p);
perm->mask.s_addr = htonl(n_bits ? (0xffffffff << (32 - n_bits)) : 0);
}
else
{
*q = '\0';
if(!inet_aton(p, &perm->address))
return -1;
perm->mask.s_addr = 0xffffffff;
}
p = q + 1;
/* fourth token: iport or iport_min-iport_max */
while(isspace(*p))
p++;
if(!isdigit(*p))
return -1;
for(q = p; isdigit(*q); q++);
if(*q=='-')
{
*q = '\0';
perm->iport_min = (u_short)atoi(p);
q++;
p = q;
while(isdigit(*q))
q++;
*q = '\0';
perm->iport_max = (u_short)atoi(p);
}
else
{
*q = '\0';
perm->iport_min = perm->iport_max = (u_short)atoi(p);
}
#ifdef DEBUG
printf("perm rule added : %s %hu-%hu %08x/%08x %hu-%hu\n",
(perm->type==UPNPPERM_ALLOW)?"allow":"deny",
perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr),
ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max);
#endif
return 0;
}
#ifdef USE_MINIUPNPDCTL
void
write_permlist(int fd, const struct upnpperm * permary,
int nperms)
{
int l;
const struct upnpperm * perm;
int i;
char buf[128];
write(fd, "Permissions :\n", 14);
for(i = 0; i<nperms; i++)
{
perm = permary + i;
l = snprintf(buf, sizeof(buf), "%02d %s %hu-%hu %08x/%08x %hu-%hu\n",
i,
(perm->type==UPNPPERM_ALLOW)?"allow":"deny",
perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr),
ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max);
if(l<0)
return;
write(fd, buf, l);
}
}
#endif
/* match_permission()
* returns: 1 if eport, address, iport matches the permission rule
* 0 if no match */
static int
match_permission(const struct upnpperm * perm,
u_short eport, struct in_addr address, u_short iport)
{
if( (eport < perm->eport_min) || (perm->eport_max < eport))
return 0;
if( (iport < perm->iport_min) || (perm->iport_max < iport))
return 0;
if( (address.s_addr & perm->mask.s_addr)
!= (perm->address.s_addr & perm->mask.s_addr) )
return 0;
return 1;
}
int
check_upnp_rule_against_permissions(const struct upnpperm * permary,
int n_perms,
u_short eport, struct in_addr address,
u_short iport)
{
int i;
for(i=0; i<n_perms; i++)
{
if(match_permission(permary + i, eport, address, iport))
{
syslog(LOG_DEBUG,
"UPnP permission rule %d matched : port mapping %s",
i, (permary[i].type == UPNPPERM_ALLOW)?"accepted":"rejected"
);
return (permary[i].type == UPNPPERM_ALLOW);
}
}
syslog(LOG_DEBUG, "no permission rule matched : accept by default (n_perms=%d)", n_perms);
return 1; /* Default : accept */
}

View File

@ -0,0 +1,55 @@
/* $Id: upnppermissions.h,v 1.7 2007/02/28 18:13:18 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPPERMISSIONS_H__
#define __UPNPPERMISSIONS_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "config.h"
/* UPnP permission rule samples:
* allow 1024-65535 192.168.3.0/24 1024-65535
* deny 0-65535 192.168.1.125/32 0-65535 */
struct upnpperm {
enum {UPNPPERM_ALLOW=1, UPNPPERM_DENY=2 } type;
/* is it an allow or deny permission rule ? */
u_short eport_min, eport_max; /* external port range */
struct in_addr address, mask; /* ip/mask */
u_short iport_min, iport_max; /* internal port range */
};
/* read_permission_line()
* returns: 0 line read okay
* -1 error reading line
*
* line sample :
* allow 1024-65535 192.168.3.0/24 1024-65535
* allow 22 192.168.4.33/32 22
* deny 0-65535 0.0.0.0/0 0-65535 */
int
read_permission_line(struct upnpperm * perm,
char * p);
/* check_upnp_rule_against_permissions()
* returns: 0 if the upnp rule should be rejected,
* 1 if it could be accepted */
int
check_upnp_rule_against_permissions(const struct upnpperm * permary,
int n_perms,
u_short eport, struct in_addr address,
u_short iport);
#ifdef USE_MINIUPNPDCTL
void
write_permlist(int fd, const struct upnpperm * permary,
int nperms);
#endif
#endif

1135
miniupnpd/upnpredirect.c Normal file

File diff suppressed because it is too large Load Diff

173
miniupnpd/upnpredirect.h Normal file
View File

@ -0,0 +1,173 @@
/* $Id: upnpredirect.h,v 1.24 2011/06/22 20:34:39 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPREDIRECT_H__
#define __UPNPREDIRECT_H__
/* for u_int64_t */
#include <sys/types.h>
#include "config.h"
#ifdef ENABLE_LEASEFILE
int reload_from_lease_file(void);
#endif
/* upnp_redirect()
* calls OS/fw dependant implementation of the redirection.
* protocol should be the string "TCP" or "UDP"
* returns: 0 on success
* -1 failed to redirect
* -2 already redirected
* -3 permission check failed
*/
int
upnp_redirect(const char * rhost, unsigned short eport,
const char * iaddr, unsigned short iport,
const char * protocol, const char * desc,
unsigned int leaseduration);
/* upnp_redirect_internal()
* same as upnp_redirect() without any check */
int
upnp_redirect_internal(const char * rhost, unsigned short eport,
const char * iaddr, unsigned short iport,
int proto, const char * desc,
unsigned int timestamp);
/* upnp_get_redirection_infos()
* returns : 0 on success
* -1 failed to get the port mapping entry or no entry exists */
int
upnp_get_redirection_infos(unsigned short eport, const char * protocol,
unsigned short * iport, char * iaddr, int iaddrlen,
char * desc, int desclen,
char * rhost, int rhostlen,
unsigned int * leaseduration);
/* upnp_get_redirection_infos_by_index()
* returns : 0 on success
* -1 failed to get the port mapping or index out of range */
int
upnp_get_redirection_infos_by_index(int index,
unsigned short * eport, char * protocol,
unsigned short * iport,
char * iaddr, int iaddrlen,
char * desc, int desclen,
char * rhost, int rhostlen,
unsigned int * leaseduration);
/* upnp_delete_redirection()
* returns: 0 on success
* -1 on failure*/
int
upnp_delete_redirection(unsigned short eport, const char * protocol);
/* _upnp_delete_redir()
* same as above */
int
_upnp_delete_redir(unsigned short eport, int proto);
/* Periodic cleanup functions
*/
struct rule_state
{
u_int64_t packets;
u_int64_t bytes;
struct rule_state * next;
unsigned short eport;
unsigned char proto;
unsigned char to_remove;
};
/* return a linked list of all rules
* or an empty list if there are not enough
* As a "side effect", delete rules which are expired */
struct rule_state *
get_upnp_rules_state_list(int max_rules_number_target);
/* return the number of port mapping entries */
int
upnp_get_portmapping_number_of_entries(void);
/* remove_unused_rules() :
* also free the list */
void
remove_unused_rules(struct rule_state * list);
/* upnp_get_portmappings_in_range()
* return a list of all "external" ports for which a port
* mapping exists */
unsigned short *
upnp_get_portmappings_in_range(unsigned short startport,
unsigned short endport,
const char * protocol,
unsigned int * number);
#ifdef ENABLE_6FC_SERVICE
/* function to be used by WANIPv6_FirewallControl implementation */
/* retreive outbound pinhole timeout*/
int
upnp_check_outbound_pinhole(int proto, int * timeout);
/* add an inbound pinehole
* return value :
* 1 = success
* -1 = Pinhole space exhausted
* .. = error */
int
upnp_add_inboundpinhole(const char * raddr, unsigned short rport,
const char * iaddr, unsigned short iport,
const char * protocol, const char * leaseTime, int * uid);
int
upnp_add_inboundpinhole_internal(const char * raddr, unsigned short rport,
const char * iaddr, unsigned short iport,
const char * proto, int * uid);
/*
* return values :
* -4 not found
* -5 in another table
* -6 in another chain
* -7 in a chain but not a rule. (chain policy)
* */
int
upnp_get_pinhole_info(const char * raddr, unsigned short rport, char * iaddr, unsigned short * iport, char * proto, const char * uid, char * lt);
/* update the lease time */
int
upnp_update_inboundpinhole(const char * uid, const char * leasetime);
/* remove the inbound pinhole */
int
upnp_delete_inboundpinhole(const char * uid);
/* ... */
int
upnp_check_pinhole_working(const char * uid, char * eaddr, char * iaddr, unsigned short * eport, unsigned short * iport, char * protocol, int * rulenum_used);
/* number of packets that went through the pinhole */
int
upnp_get_pinhole_packets(const char * uid, int * packets);
/* ? */
int
upnp_clean_expiredpinhole(void);
#endif /* ENABLE_6FC_SERVICE */
/* stuff for responding to miniupnpdctl */
#ifdef USE_MINIUPNPDCTL
void
write_ruleset_details(int s);
#endif
#endif

152
miniupnpd/upnpreplyparse.c Normal file
View File

@ -0,0 +1,152 @@
/* $Id: upnpreplyparse.c,v 1.11 2011/02/07 16:17:06 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "upnpreplyparse.h"
#include "minixml.h"
static void
NameValueParserStartElt(void * d, const char * name, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
if(l>63)
l = 63;
memcpy(data->curelt, name, l);
data->curelt[l] = '\0';
}
static void
NameValueParserGetData(void * d, const char * datas, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
struct NameValue * nv;
if(strcmp(data->curelt, "NewPortListing") == 0)
{
/* specific case for NewPortListing which is a XML Document */
data->portListing = malloc(l + 1);
if(!data->portListing)
{
/* malloc error */
return;
}
memcpy(data->portListing, datas, l);
data->portListing[l] = '\0';
data->portListingLength = l;
}
else
{
/* standard case. Limited to 63 chars strings */
nv = malloc(sizeof(struct NameValue));
if(l>63)
l = 63;
strncpy(nv->name, data->curelt, 64);
nv->name[63] = '\0';
memcpy(nv->value, datas, l);
nv->value[l] = '\0';
LIST_INSERT_HEAD( &(data->head), nv, entries);
}
}
void
ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data)
{
struct xmlparser parser;
LIST_INIT(&(data->head));
data->portListing = NULL;
data->portListingLength = 0;
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = data;
parser.starteltfunc = NameValueParserStartElt;
parser.endeltfunc = 0;
parser.datafunc = NameValueParserGetData;
parser.attfunc = 0;
parsexml(&parser);
}
void
ClearNameValueList(struct NameValueParserData * pdata)
{
struct NameValue * nv;
if(pdata->portListing)
{
free(pdata->portListing);
pdata->portListing = NULL;
pdata->portListingLength = 0;
}
while((nv = pdata->head.lh_first) != NULL)
{
LIST_REMOVE(nv, entries);
free(nv);
}
}
char *
GetValueFromNameValueList(struct NameValueParserData * pdata,
const char * Name)
{
struct NameValue * nv;
char * p = NULL;
for(nv = pdata->head.lh_first;
(nv != NULL) && (p == NULL);
nv = nv->entries.le_next)
{
if(strcmp(nv->name, Name) == 0)
p = nv->value;
}
return p;
}
#if 0
/* useless now that minixml ignores namespaces by itself */
char *
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
const char * Name)
{
struct NameValue * nv;
char * p = NULL;
char * pname;
for(nv = pdata->head.lh_first;
(nv != NULL) && (p == NULL);
nv = nv->entries.le_next)
{
pname = strrchr(nv->name, ':');
if(pname)
pname++;
else
pname = nv->name;
if(strcmp(pname, Name)==0)
p = nv->value;
}
return p;
}
#endif
/* debug all-in-one function
* do parsing then display to stdout */
#ifdef DEBUG
void
DisplayNameValueList(char * buffer, int bufsize)
{
struct NameValueParserData pdata;
struct NameValue * nv;
ParseNameValue(buffer, bufsize, &pdata);
for(nv = pdata.head.lh_first;
nv != NULL;
nv = nv->entries.le_next)
{
printf("%s = %s\n", nv->name, nv->value);
}
ClearNameValueList(&pdata);
}
#endif

View File

@ -0,0 +1,64 @@
/* $Id: upnpreplyparse.h,v 1.11 2011/02/07 16:17:06 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPREPLYPARSE_H__
#define __UPNPREPLYPARSE_H__
#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__)
#include "bsdqueue.h"
#else
#include <sys/queue.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct NameValue {
LIST_ENTRY(NameValue) entries;
char name[64];
char value[64];
};
struct NameValueParserData {
LIST_HEAD(listhead, NameValue) head;
char curelt[64];
char * portListing;
int portListingLength;
};
/* ParseNameValue() */
void
ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data);
/* ClearNameValueList() */
void
ClearNameValueList(struct NameValueParserData * pdata);
/* GetValueFromNameValueList() */
char *
GetValueFromNameValueList(struct NameValueParserData * pdata,
const char * Name);
/* GetValueFromNameValueListIgnoreNS() */
char *
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
const char * Name);
/* DisplayNameValueList() */
#ifdef DEBUG
void
DisplayNameValueList(char * buffer, int bufsize);
#endif
#ifdef __cplusplus
}
#endif
#endif

1870
miniupnpd/upnpsoap.c Normal file

File diff suppressed because it is too large Load Diff

23
miniupnpd/upnpsoap.h Normal file
View File

@ -0,0 +1,23 @@
/* $Id: upnpsoap.h,v 1.8 2007/02/07 22:16:19 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPSOAP_H__
#define __UPNPSOAP_H__
/* ExecuteSoapAction():
* this method executes the requested Soap Action */
void
ExecuteSoapAction(struct upnphttp *, const char *, int);
/* SoapError():
* sends a correct SOAP error with an UPNPError code and
* description */
void
SoapError(struct upnphttp * h, int errCode, const char * errDesc);
#endif

30
miniupnpd/upnpurns.h Normal file
View File

@ -0,0 +1,30 @@
/* $Id: upnpurns.h,v 1.1 2011/05/13 15:32:53 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPURNS_H__
#define __UPNPURNS_H__
#include "config.h"
#ifdef IGD_V2
/* IGD v2 */
#define DEVICE_TYPE_IGD "urn:schemas-upnp-org:device:InternetGatewayDevice:2"
#define DEVICE_TYPE_WAN "urn:schemas-upnp-org:device:WANDevice:2"
#define DEVICE_TYPE_WANC "urn:schemas-upnp-org:device:WANConnectionDevice:2"
#define SERVICE_TYPE_WANIPC "urn:schemas-upnp-org:service:WANIPConnection:2"
#define SERVICE_ID_WANIPC "urn:upnp-org:serviceId:WANIPConn1"
#else
/* IGD v1 */
#define DEVICE_TYPE_IGD "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
#define DEVICE_TYPE_WAN "urn:schemas-upnp-org:device:WANDevice:1"
#define DEVICE_TYPE_WANC "urn:schemas-upnp-org:device:WANConnectionDevice:1"
#define SERVICE_TYPE_WANIPC "urn:schemas-upnp-org:service:WANIPConnection:1"
#define SERVICE_ID_WANIPC "urn:upnp-org:serviceId:WANIPConn1"
#endif
#endif

68
miniupnpd/upnputils.c Normal file
View File

@ -0,0 +1,68 @@
/* $Id: upnputils.c,v 1.3 2011/05/20 09:42:23 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef AF_LINK
#include <net/if_dl.h>
#endif
#include "upnputils.h"
int
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size)
{
char buffer[64];
unsigned short port = 0;
int n = -1;
switch(addr->sa_family)
{
case AF_INET6:
inet_ntop(addr->sa_family,
&((struct sockaddr_in6 *)addr)->sin6_addr,
buffer, sizeof(buffer));
port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
n = snprintf(str, size, "[%s]:%hu", buffer, port);
break;
case AF_INET:
inet_ntop(addr->sa_family,
&((struct sockaddr_in *)addr)->sin_addr,
buffer, sizeof(buffer));
port = ntohs(((struct sockaddr_in *)addr)->sin_port);
n = snprintf(str, size, "%s:%hu", buffer, port);
break;
#ifdef AF_LINK
case AF_LINK:
{
struct sockaddr_dl * sdl = (struct sockaddr_dl *)addr;
n = snprintf(str, size, "index=%hu type=%d %s",
sdl->sdl_index, sdl->sdl_type,
link_ntoa(sdl));
}
break;
#endif
default:
n = snprintf(str, size, "unknown address family %d", addr->sa_family);
#if 0
n = snprintf(str, size, "unknown address family %d "
"%02x %02x %02x %02x %02x %02x %02x %02x",
addr->sa_family,
addr->sa_data[0], addr->sa_data[1], (unsigned)addr->sa_data[2], addr->sa_data[3],
addr->sa_data[4], addr->sa_data[5], (unsigned)addr->sa_data[6], addr->sa_data[7]);
#endif
}
return n;
}

20
miniupnpd/upnputils.h Normal file
View File

@ -0,0 +1,20 @@
/* $Id: upnputils.h,v 1.1 2011/05/15 08:58:41 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef __UPNPUTILS_H__
#define __UPNPUTILS_H__
/**
* convert a struct sockaddr to a human readable string.
* [ipv6]:port or ipv4:port
* return the number of characters used (as snprintf)
*/
int
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size);
#endif