summaryrefslogtreecommitdiff
path: root/release/src/router/busybox/networking
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/router/busybox/networking')
-rw-r--r--[-rwxr-xr-x]release/src/router/busybox/networking/Config.in974
-rw-r--r--release/src/router/busybox/networking/Kbuild46
-rw-r--r--release/src/router/busybox/networking/Makefile30
-rwxr-xr-xrelease/src/router/busybox/networking/Makefile.in65
-rw-r--r--release/src/router/busybox/networking/arp.c515
-rw-r--r--release/src/router/busybox/networking/arping.c545
-rw-r--r--release/src/router/busybox/networking/brctl.c286
-rw-r--r--release/src/router/busybox/networking/dnsd.c518
-rw-r--r--release/src/router/busybox/networking/ether-wake.c276
-rw-r--r--release/src/router/busybox/networking/ftpd.c1351
-rw-r--r--release/src/router/busybox/networking/ftpgetput.c476
-rw-r--r--release/src/router/busybox/networking/hostname.c133
-rw-r--r--release/src/router/busybox/networking/httpd.c3830
-rw-r--r--release/src/router/busybox/networking/httpd_indexcgi.c342
-rw-r--r--release/src/router/busybox/networking/httpd_post_upload.txt76
-rw-r--r--release/src/router/busybox/networking/ifconfig.c408
-rw-r--r--release/src/router/busybox/networking/ifenslave.c592
-rw-r--r--release/src/router/busybox/networking/ifupdown.c1578
-rw-r--r--release/src/router/busybox/networking/inetd.c2310
-rw-r--r--release/src/router/busybox/networking/interface.c1292
-rw-r--r--release/src/router/busybox/networking/ip.c180
-rw-r--r--release/src/router/busybox/networking/ipaddr.c27
-rw-r--r--release/src/router/busybox/networking/ipcalc.c208
-rw-r--r--release/src/router/busybox/networking/iplink.c27
-rw-r--r--release/src/router/busybox/networking/iproute.c27
-rw-r--r--release/src/router/busybox/networking/iptunnel.c27
-rw-r--r--release/src/router/busybox/networking/isrv.c338
-rw-r--r--release/src/router/busybox/networking/isrv.h37
-rw-r--r--release/src/router/busybox/networking/isrv_identd.c147
-rw-r--r--release/src/router/busybox/networking/libiproute/Kbuild64
-rw-r--r--release/src/router/busybox/networking/libiproute/Makefile30
-rwxr-xr-xrelease/src/router/busybox/networking/libiproute/Makefile.in44
-rw-r--r--release/src/router/busybox/networking/libiproute/ip_common.h49
-rw-r--r--release/src/router/busybox/networking/libiproute/ip_parse_common_args.c98
-rw-r--r--release/src/router/busybox/networking/libiproute/ipaddress.c408
-rw-r--r--release/src/router/busybox/networking/libiproute/iplink.c334
-rw-r--r--release/src/router/busybox/networking/libiproute/iproute.c608
-rw-r--r--release/src/router/busybox/networking/libiproute/iprule.c330
-rw-r--r--release/src/router/busybox/networking/libiproute/iptunnel.c448
-rw-r--r--release/src/router/busybox/networking/libiproute/libnetlink.c347
-rw-r--r--release/src/router/busybox/networking/libiproute/libnetlink.h60
-rw-r--r--release/src/router/busybox/networking/libiproute/linux/pkt_sched.h413
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_addr.c54
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_map.c146
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_map.h29
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_proto.c121
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_types.c250
-rw-r--r--release/src/router/busybox/networking/libiproute/rt_names.c320
-rw-r--r--release/src/router/busybox/networking/libiproute/rt_names.h43
-rw-r--r--release/src/router/busybox/networking/libiproute/rtm_map.c44
-rw-r--r--release/src/router/busybox/networking/libiproute/rtm_map.h14
-rw-r--r--release/src/router/busybox/networking/libiproute/utils.c290
-rw-r--r--release/src/router/busybox/networking/libiproute/utils.h97
-rw-r--r--release/src/router/busybox/networking/nameif.c342
-rw-r--r--release/src/router/busybox/networking/nc.c288
-rw-r--r--release/src/router/busybox/networking/nc_bloaty.c832
-rw-r--r--release/src/router/busybox/networking/netstat.c1005
-rw-r--r--release/src/router/busybox/networking/nslookup.c287
-rw-r--r--release/src/router/busybox/networking/ping.c896
-rw-r--r--release/src/router/busybox/networking/ping6.c515
-rw-r--r--release/src/router/busybox/networking/pscan.c154
-rw-r--r--release/src/router/busybox/networking/route.c1034
-rw-r--r--release/src/router/busybox/networking/slattach.c245
-rw-r--r--release/src/router/busybox/networking/tc.c543
-rw-r--r--release/src/router/busybox/networking/tcpudp.c607
-rw-r--r--release/src/router/busybox/networking/tcpudp_perhost.c65
-rw-r--r--release/src/router/busybox/networking/tcpudp_perhost.h33
-rw-r--r--release/src/router/busybox/networking/telnet.c736
-rw-r--r--release/src/router/busybox/networking/telnetd.c1048
-rw-r--r--release/src/router/busybox/networking/telnetd.ctrlSQ.patch175
-rw-r--r--release/src/router/busybox/networking/tftp.c1119
-rw-r--r--release/src/router/busybox/networking/traceroute.c1200
-rw-r--r--release/src/router/busybox/networking/tunctl.c139
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/AUTHORS14
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/COPYING339
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/ChangeLog262
-rw-r--r--[-rwxr-xr-x]release/src/router/busybox/networking/udhcp/Config.in126
-rw-r--r--release/src/router/busybox/networking/udhcp/Kbuild25
-rw-r--r--release/src/router/busybox/networking/udhcp/Makefile30
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/Makefile.in61
-rw-r--r--release/src/router/busybox/networking/udhcp/README50
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/README.dumpleases17
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/README.udhcpc141
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/README.udhcpd59
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/TODO14
-rw-r--r--release/src/router/busybox/networking/udhcp/arpping.c162
-rw-r--r--release/src/router/busybox/networking/udhcp/arpping.h30
-rw-r--r--release/src/router/busybox/networking/udhcp/clientpacket.c280
-rw-r--r--release/src/router/busybox/networking/udhcp/clientpacket.h12
-rw-r--r--release/src/router/busybox/networking/udhcp/clientsocket.c108
-rw-r--r--release/src/router/busybox/networking/udhcp/common.c147
-rw-r--r--release/src/router/busybox/networking/udhcp/common.h132
-rw-r--r--release/src/router/busybox/networking/udhcp/debug.h41
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpc.c924
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpc.h65
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpd.c347
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpd.h218
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcprelay.c325
-rw-r--r--release/src/router/busybox/networking/udhcp/domain_codec.c205
-rw-r--r--release/src/router/busybox/networking/udhcp/dumpleases.c149
-rw-r--r--release/src/router/busybox/networking/udhcp/files.c568
-rw-r--r--release/src/router/busybox/networking/udhcp/files.h17
-rw-r--r--release/src/router/busybox/networking/udhcp/frontend.c16
-rw-r--r--release/src/router/busybox/networking/udhcp/leases.c210
-rw-r--r--release/src/router/busybox/networking/udhcp/leases.h25
-rw-r--r--release/src/router/busybox/networking/udhcp/leases_file.c1
-rw-r--r--release/src/router/busybox/networking/udhcp/libbb_udhcp.h29
-rw-r--r--release/src/router/busybox/networking/udhcp/options.c363
-rw-r--r--release/src/router/busybox/networking/udhcp/options.h110
-rw-r--r--release/src/router/busybox/networking/udhcp/packet.c269
-rw-r--r--release/src/router/busybox/networking/udhcp/packet.h41
-rw-r--r--release/src/router/busybox/networking/udhcp/pidfile.c69
-rw-r--r--release/src/router/busybox/networking/udhcp/pidfile.h26
-rw-r--r--release/src/router/busybox/networking/udhcp/script.c256
-rw-r--r--release/src/router/busybox/networking/udhcp/script.h6
-rw-r--r--release/src/router/busybox/networking/udhcp/serverpacket.c223
-rw-r--r--release/src/router/busybox/networking/udhcp/serverpacket.h11
-rw-r--r--release/src/router/busybox/networking/udhcp/signalpipe.c82
-rw-r--r--release/src/router/busybox/networking/udhcp/socket.c142
-rw-r--r--release/src/router/busybox/networking/udhcp/socket.h9
-rw-r--r--release/src/router/busybox/networking/udhcp/static_leases.c79
-rw-r--r--release/src/router/busybox/networking/udhcp/version.h6
-rw-r--r--release/src/router/busybox/networking/vconfig.c112
-rw-r--r--release/src/router/busybox/networking/wget.c1435
-rw-r--r--release/src/router/busybox/networking/zcip.c567
125 files changed, 25834 insertions, 15686 deletions
diff --git a/release/src/router/busybox/networking/Config.in b/release/src/router/busybox/networking/Config.in
index 42f8a79c..392afcf8 100755..100644
--- a/release/src/router/busybox/networking/Config.in
+++ b/release/src/router/busybox/networking/Config.in
@@ -5,569 +5,937 @@
menu "Networking Utilities"
-config CONFIG_FEATURE_IPV6
+config FEATURE_IPV6
bool "Enable IPv6 support"
default n
help
- Enable IPv6 support to busybox. This makes applets that talk IP
- able to work with IPv6.
+ Enable IPv6 support in busybox.
+ This adds IPv6 support in the networking applets.
-config CONFIG_ARPING
+config FEATURE_PREFER_IPV4_ADDRESS
+ bool "Prefer IPv4 addresses from DNS queries"
+ default y
+ depends on FEATURE_IPV6
+ help
+ Use IPv4 address of network host if it has one.
+
+ If this option is off, the first returned address will be used.
+ This may cause problems when your DNS server is IPv6-capable and
+ is returning IPv6 host addresses too. If IPv6 address
+ precedes IPv4 one in DNS reply, busybox network applets
+ (e.g. wget) will use IPv6 address. On an IPv6-incapable host
+ or network applets will fail to connect to the host
+ using IPv6 address.
+
+config VERBOSE_RESOLUTION_ERRORS
+ bool "Verbose resolution errors"
+ default n
+ help
+ Enable if you are not satisfied with simplistic
+ "can't resolve 'hostname.com'" and want to know more.
+ This may increase size of your executable a bit.
+
+config ARP
+ bool "arp"
+ default n
+ help
+ Manipulate the system ARP cache.
+
+config ARPING
bool "arping"
default n
help
- Ping hosts by ARP packets
+ Ping hosts by ARP packets.
-config CONFIG_FTPGET
- bool "ftpget"
+config BRCTL
+ bool "brctl"
default n
help
- Retrieve a remote file via FTP.
+ Manage ethernet bridges.
+ Supports addbr/delbr and addif/delif.
-config CONFIG_FTPPUT
- bool "ftpput"
+config FEATURE_BRCTL_FANCY
+ bool "Fancy options"
default n
+ depends on BRCTL
help
- Store a remote file via FTP.
+ Add support for extended option like:
+ setageing, setfd, sethello, setmaxage,
+ setpathcost, setportprio, setbridgeprio,
+ stp
+ This adds about 600 bytes.
-config CONFIG_HOSTNAME
- bool "hostname"
+config FEATURE_BRCTL_SHOW
+ bool "Support show, showmac and showstp"
default n
+ depends on BRCTL && FEATURE_BRCTL_FANCY
help
- Show or set the system's host name
+ Add support for option which prints the current config:
+ showmacs, showstp, show
-config CONFIG_HTTPD
- bool "httpd"
+config DNSD
+ bool "dnsd"
default n
help
- Serve web pages via an HTTP server.
+ Small and static DNS server daemon.
+
+config ETHER_WAKE
+ bool "ether-wake"
+ default n
+ help
+ Send a magic packet to wake up sleeping machines.
+
+config FAKEIDENTD
+ bool "fakeidentd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ fakeidentd listens on the ident port and returns a predefined
+ fake value on any query.
-config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- bool " Support using httpd only from inetd"
+config FTPD
+ bool "ftpd"
default n
- depends on CONFIG_HTTPD
help
- This option disables uid and port options for the httpd applet
- but requires inetd server daemon.
+ simple FTP daemon. You have to run it via inetd.
-config CONFIG_FEATURE_HTTPD_BASIC_AUTH
- bool " Enable Basic http Authentication"
+config FEATURE_FTP_WRITE
+ bool "Enable upload commands"
default y
- depends on CONFIG_HTTPD
+ depends on FTPD
help
- Utilizes password settings from /etc/httpd.conf for basic
- authentication on a per url basis.
+ Enable all kinds of FTP upload commands (-w option)
-config CONFIG_FEATURE_HTTPD_AUTH_MD5
- bool " Support MD5 crypted passwords for http Authentication"
+config FTPGET
+ bool "ftpget"
default n
- depends on CONFIG_FEATURE_HTTPD_BASIC_AUTH
help
- Enables basic per url authentication from /etc/httpd.conf
- using md5 passwords.
+ Retrieve a remote file via FTP.
+
+config FTPPUT
+ bool "ftpput"
+ default n
+ help
+ Store a remote file via FTP.
+
+config FEATURE_FTPGETPUT_LONG_OPTIONS
+ bool "Enable long options in ftpget/ftpput"
+ default n
+ depends on GETOPT_LONG && (FTPGET || FTPPUT)
+ help
+ Support long options for the ftpget/ftpput applet.
+
+config HOSTNAME
+ bool "hostname"
+ default n
+ help
+ Show or set the system's host name.
+
+config HTTPD
+ bool "httpd"
+ default n
+ help
+ Serve web pages via an HTTP server.
+config FEATURE_HTTPD_RANGES
+ bool "Support 'Ranges:' header"
+ default n
+ depends on HTTPD
+ help
+ Makes httpd emit "Accept-Ranges: bytes" header and understand
+ "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted
+ downloads, seeking in multimedia players etc.
-if !CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-config CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
- bool " Support reloading the global config file using hup signal"
+config FEATURE_HTTPD_USE_SENDFILE
+ bool "Use sendfile system call"
default n
- depends on CONFIG_HTTPD
+ depends on HTTPD
help
- This option enables processing of SIGHUP to reload cached
- configuration settings.
+ When enabled, httpd will use the kernel sendfile() function
+ instead of read/write loop.
-config CONFIG_FEATURE_HTTPD_SETUID
- bool " Enable support -u <user> option"
+config FEATURE_HTTPD_SETUID
+ bool "Enable -u <user> option"
default n
- depends on CONFIG_HTTPD
+ depends on HTTPD
help
This option allows the server to run as a specific user
rather than defaulting to the user that starts the server.
- Use of this option requires special privilegies to change to a
+ Use of this option requires special privileges to change to a
different user.
-endif
-config CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- bool " Support loading additional mime types at run-time"
+config FEATURE_HTTPD_BASIC_AUTH
+ bool "Enable Basic http Authentication"
+ default y
+ depends on HTTPD
+ help
+ Utilizes password settings from /etc/httpd.conf for basic
+ authentication on a per url basis.
+
+config FEATURE_HTTPD_AUTH_MD5
+ bool "Support MD5 crypted passwords for http Authentication"
default n
- depends on CONFIG_HTTPD
+ depends on FEATURE_HTTPD_BASIC_AUTH
help
- This option enables support for additional mime types at
- run-time to be specified in the configuration file.
+ Enables basic per URL authentication from /etc/httpd.conf
+ using md5 passwords.
-config CONFIG_FEATURE_HTTPD_CGI
- bool " Support Common Gateway Interface (CGI)"
+config FEATURE_HTTPD_CGI
+ bool "Support Common Gateway Interface (CGI)"
default y
- depends on CONFIG_HTTPD
+ depends on HTTPD
help
This option allows scripts and executables to be invoked
- when specific urls are requested.
+ when specific URLs are requested.
-config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
- bool " Support the REMOTE_PORT environment variable for CGI"
+config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ bool "Support for running scripts through an interpreter"
default n
- depends on CONFIG_FEATURE_HTTPD_CGI
+ depends on FEATURE_HTTPD_CGI
help
- Use of this option can assist scripts in generating
- references that contain a unique port number.
+ This option enables support for running scripts through an
+ interpreter. Turn this on if you want PHP scripts to work
+ properly. You need to supply an additional line in your httpd
+ config file:
+ *.php:/path/to/your/php
-config CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
- bool " Enable setting of CGI_varname=value environment vars for CGI"
+config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ bool "Set REMOTE_PORT environment variable for CGI"
default n
- depends on CONFIG_FEATURE_HTTPD_CGI
+ depends on FEATURE_HTTPD_CGI
help
- This option parses POST or GET arguments from a form and
- sets environment variables with their value. This simplifies
- and speeds up CGI scripts. A form argument of foo=bar would
- result in a script having the environment variable CGI_foo set
- to 'bar'. In addition, this option sets a variable that
- lists all the argument names. e.g. CGI_VARNAMES_="name1 name2".
+ Use of this option can assist scripts in generating
+ references that contain a unique port number.
-config CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
- bool " Enable the -e option for shell script CGI simplification."
+config FEATURE_HTTPD_ENCODE_URL_STR
+ bool "Enable -e option (useful for CGIs written as shell scripts)"
default y
- depends on CONFIG_HTTPD
+ depends on HTTPD
help
- After set, this option allows html encoding arbitrary
- strings for display of the browser. Output goes to stdout.
- For example, httpd -e "<Hello World>" as
+ This option allows html encoding of arbitrary strings for display
+ by the browser. Output goes to stdout.
+ For example, httpd -e "<Hello World>" produces
"&#60Hello&#32World&#62".
-config CONFIG_IFCONFIG
+config FEATURE_HTTPD_ERROR_PAGES
+ bool "Support for custom error pages"
+ default n
+ depends on HTTPD
+ help
+ This option allows you to define custom error pages in
+ the configuration file instead of the default HTTP status
+ error pages. For instance, if you add the line:
+ E404:/path/e404.html
+ in the config file, the server will respond the specified
+ '/path/e404.html' file instead of the terse '404 NOT FOUND'
+ message.
+
+config FEATURE_HTTPD_PROXY
+ bool "Support for reverse proxy"
+ default n
+ depends on HTTPD
+ help
+ This option allows you to define URLs that will be forwarded
+ to another HTTP server. To setup add the following line to the
+ configuration file
+ P:/url/:http://hostname[:port]/new/path/
+ Then a request to /url/myfile will be forwarded to
+ http://hostname[:port]/new/path/myfile.
+
+config IFCONFIG
bool "ifconfig"
default n
help
Ifconfig is used to configure the kernel-resident network interfaces.
-config CONFIG_FEATURE_IFCONFIG_STATUS
- bool " Enable status reporting output (+7k)"
+config FEATURE_IFCONFIG_STATUS
+ bool "Enable status reporting output (+7k)"
default y
- depends on CONFIG_IFCONFIG
+ depends on IFCONFIG
help
If ifconfig is called with no arguments it will display the status
of the currently active interfaces.
-config CONFIG_FEATURE_IFCONFIG_SLIP
- bool " Enable slip-specific options \"keepalive\" and \"outfill\""
+config FEATURE_IFCONFIG_SLIP
+ bool "Enable slip-specific options \"keepalive\" and \"outfill\""
default n
- depends on CONFIG_IFCONFIG
+ depends on IFCONFIG
help
- Allow "keepalive" and "outfill" support for SLIP. If you're not
+ Allow "keepalive" and "outfill" support for SLIP. If you're not
planning on using serial lines, leave this unchecked.
-config CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
- bool " Enable options \"mem_start\", \"io_addr\", and \"irq\""
+config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ bool "Enable options \"mem_start\", \"io_addr\", and \"irq\""
default n
- depends on CONFIG_IFCONFIG
+ depends on IFCONFIG
help
Allow the start address for shared memory, start address for I/O,
and/or the interrupt line used by the specified device.
-config CONFIG_FEATURE_IFCONFIG_HW
- bool " Enable option \"hw\" (ether only)"
+config FEATURE_IFCONFIG_HW
+ bool "Enable option \"hw\" (ether only)"
default y
- depends on CONFIG_IFCONFIG
+ depends on IFCONFIG
help
Set the hardware address of this interface, if the device driver
- supports this operation. Currently, we only support the 'ether'
+ supports this operation. Currently, we only support the 'ether'
class.
-config CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
- bool " Set the broadcast automatically"
+config FEATURE_IFCONFIG_BROADCAST_PLUS
+ bool "Set the broadcast automatically"
default n
- depends on CONFIG_IFCONFIG
+ depends on IFCONFIG
help
Setting this will make ifconfig attempt to find the broadcast
automatically if the value '+' is used.
-config CONFIG_IFUPDOWN
+config IFENSLAVE
+ bool "ifenslave"
+ default n
+ help
+ Userspace application to bind several interfaces
+ to a logical interface (use with kernel bonding driver).
+
+config IFUPDOWN
bool "ifupdown"
default n
help
- Activate or deactivate the specified interfaces. This applet makes
+ Activate or deactivate the specified interfaces. This applet makes
use of either "ifconfig" and "route" or the "ip" command to actually
- configure network interfaces. Therefore, you will probably also want
- to enable either CONFIG_IFCONFIG and CONFIG_ROUTE, or enable
- CONFIG_FEATURE_IFUPDOWN_IP and the various CONFIG_IP options. Of
+ configure network interfaces. Therefore, you will probably also want
+ to enable either IFCONFIG and ROUTE, or enable
+ FEATURE_IFUPDOWN_IP and the various IP options. Of
course you could use non-busybox versions of these programs, so
against my better judgement (since this will surely result in plenty
of support questions on the mailing list), I do not force you to
- enable these additional options. It is up to you to supply either
- "ifconfig" and "route" or the "ip" command, either via busybox or via
- standalone utilities.
-
-# I really should force these to be enabled
-# && CONFIG_IP && CONFIG_FEATURE_IP_ADDRESS && CONFIG_FEATURE_IP_LINK && CONFIG_FEATURE_IP_ROUTE
-# but then people could not use the full-blown iproute2 program...
-config CONFIG_FEATURE_IFUPDOWN_IP
- bool " Use ip applet"
- default n
- depends on CONFIG_IFUPDOWN
- help
- Use the iproute "ip" command to implement "ifupdown". You will
- probably want to also enable CONFIG_IP, CONFIG_FEATURE_IP_ADDRESS,
- CONFIG_FEATURE_IP_LINK, and CONFIG_FEATURE_IP_ROUTE. Of course
- if you wanted to use the full-blown iproute2 program you could
- leave the the busybox CONFIG_IP* options disabled.
-
-config CONFIG_FEATURE_IFUPDOWN_IPV4
- bool " Enable support for IPv4"
+ enable these additional options. It is up to you to supply either
+ "ifconfig", "route" and "run-parts" or the "ip" command, either
+ via busybox or via standalone utilities.
+
+config IFUPDOWN_IFSTATE_PATH
+ string "Absolute path to ifstate file"
+ default "/var/run/ifstate"
+ depends on IFUPDOWN
+ help
+ ifupdown keeps state information in a file called ifstate.
+ Typically it is located in /var/run/ifstate, however
+ some distributions tend to put it in other places
+ (debian, for example, uses /etc/network/run/ifstate).
+ This config option defines location of ifstate.
+
+config FEATURE_IFUPDOWN_IP
+ bool "Use ip applet"
+ default n
+ depends on IFUPDOWN
+ help
+ Use the iproute "ip" command to implement "ifup" and "ifdown", rather
+ than the default of using the older 'ifconfig' and 'route' utilities.
+
+config FEATURE_IFUPDOWN_IP_BUILTIN
+ bool "Use busybox ip applet"
+ default y
+ depends on FEATURE_IFUPDOWN_IP
+ select IP
+ select FEATURE_IP_ADDRESS
+ select FEATURE_IP_LINK
+ select FEATURE_IP_ROUTE
+ help
+ Use the busybox iproute "ip" applet to implement "ifupdown".
+
+ If left disabled, you must install the full-blown iproute2
+ utility or the "ifup" and "ifdown" applets will not work.
+
+config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN
+ bool "Use busybox ifconfig and route applets"
+ default y
+ depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP
+ select IFCONFIG
+ select ROUTE
+ help
+ Use the busybox iproute "ifconfig" and "route" applets to
+ implement the "ifup" and "ifdown" utilities.
+
+ If left disabled, you must install the full-blown ifconfig
+ and route utilities, or the "ifup" and "ifdown" applets will not
+ work.
+
+config FEATURE_IFUPDOWN_IPV4
+ bool "Support for IPv4"
default y
- depends on CONFIG_IFUPDOWN
+ depends on IFUPDOWN
help
- If you want busybox to talk IPv4, leave this on.
+ If you want ifup/ifdown to talk IPv4, leave this on.
-config CONFIG_FEATURE_IFUPDOWN_IPV6
- bool " Enable support for IPv6"
+config FEATURE_IFUPDOWN_IPV6
+ bool "Support for IPv6"
default n
- depends on CONFIG_IFUPDOWN
+ depends on IFUPDOWN && FEATURE_IPV6
help
If you need support for IPv6, turn this option on.
-config CONFIG_FEATURE_IFUPDOWN_IPX
- bool " Enable support for IPX"
+### UNUSED
+###config FEATURE_IFUPDOWN_IPX
+### bool "Support for IPX"
+### default n
+### depends on IFUPDOWN
+### help
+### If this option is selected you can use busybox to work with IPX
+### networks.
+
+config FEATURE_IFUPDOWN_MAPPING
+ bool "Enable mapping support"
default n
- depends on CONFIG_IFUPDOWN
+ depends on IFUPDOWN
help
- If this option is selected you can use busybox to work with IPX
- networks.
+ This enables support for the "mapping" stanza, unless you have
+ a weird network setup you don't need it.
-config CONFIG_FEATURE_IFUPDOWN_MAPPING
- bool " Enable mapping support"
+config FEATURE_IFUPDOWN_EXTERNAL_DHCP
+ bool "Support for external dhcp clients"
default n
- depends on CONFIG_IFUPDOWN
+ depends on IFUPDOWN
help
- This enables support for the "mapping" stanza, unless you have
- a weird network setup you dont need it.
+ This enables support for the external dhcp clients. Clients are
+ tried in the following order: dhcpcd, dhclient, pump and udhcpc.
+ Otherwise, if udhcpc applet is enabled, it is used.
+ Otherwise, ifup/ifdown will have no support for DHCP.
-config CONFIG_INETD
+config INETD
bool "inetd"
default n
+ select FEATURE_SYSLOG
help
Internet superserver daemon
-config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
- bool " Support echo service"
+config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+ bool "Support echo service"
default y
- depends on CONFIG_INETD
+ depends on INETD
help
Echo received data internal inetd service
-config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
- bool " Support discard service"
+config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+ bool "Support discard service"
default y
- depends on CONFIG_INETD
+ depends on INETD
help
Internet /dev/null internal inetd service
-config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
- bool " Support time service"
+config FEATURE_INETD_SUPPORT_BUILTIN_TIME
+ bool "Support time service"
default y
- depends on CONFIG_INETD
+ depends on INETD
help
Return 32 bit time since 1900 internal inetd service
-config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
- bool " Support daytime service"
+config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+ bool "Support daytime service"
default y
- depends on CONFIG_INETD
+ depends on INETD
help
Return human-readable time internal inetd service
-config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
- bool " Support chargen service"
+config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+ bool "Support chargen service"
default y
- depends on CONFIG_INETD
+ depends on INETD
help
Familiar character generator internal inetd service
+config FEATURE_INETD_RPC
+ bool "Support RPC services"
+ default n
+ depends on INETD
+ select FEATURE_HAVE_RPC
+ help
+ Support Sun-RPC based services
-config CONFIG_IP
+config IP
bool "ip"
default n
help
The "ip" applet is a TCP/IP interface configuration and routing
- utility. You generally don't need "ip" to use busybox with
+ utility. You generally don't need "ip" to use busybox with
TCP/IP.
-if CONFIG_IP && CONFIG_IPADDR
- config CONFIG_FEATURE_IP_ADDRESS
- default y
- comment " address (forced enabled for ipaddr)"
-endif
-if ! (CONFIG_IP && CONFIG_IPADDR)
- config CONFIG_FEATURE_IP_ADDRESS
- bool " address"
- default y
- depends on CONFIG_IP
- help
- Address manipulation support for the "ip" applet.
-endif
-
-if CONFIG_IP && CONFIG_IPLINK
- config CONFIG_FEATURE_IP_LINK
- default y
- comment " link (forced enabled for iplink)"
-endif
-if !(CONFIG_IP && CONFIG_IPLINK)
- config CONFIG_FEATURE_IP_LINK
- bool " link"
- default y
- depends on CONFIG_IP
- help
- Configure network devices with "ip".
-endif
-
-if CONFIG_IP && CONFIG_IPROUTE
- config CONFIG_FEATURE_IP_ROUTE
- default y
- comment " route (forced enabled for iproute)"
-endif
-if !(CONFIG_IP && CONFIG_IPROUTE)
- config CONFIG_FEATURE_IP_ROUTE
- bool " route"
- default y
- depends on CONFIG_IP
- help
- Add support for routing table management to "ip".
-endif
-
-if CONFIG_IP && CONFIG_IPTUNNEL
- config CONFIG_FEATURE_IP_TUNNEL
- default y
- comment " tunnel (forced enabled for iptunnel)"
-endif
-if !(CONFIG_IP && CONFIG_IPTUNNEL)
- config CONFIG_FEATURE_IP_TUNNEL
- bool " tunnel"
- default n
- depends on CONFIG_IP
- help
- Add support for tunneling commands to "ip".
-endif
-
-config CONFIG_IPCALC
- bool "ipcalc"
- default n
+config FEATURE_IP_ADDRESS
+ bool "ip address"
+ default y
+ depends on IP
help
- ipcalc takes an IP address and netmask and calculates the
- resulting broadcast, network, and host range.
+ Address manipulation support for the "ip" applet.
+
+config FEATURE_IP_LINK
+ bool "ip link"
+ default y
+ depends on IP
+ help
+ Configure network devices with "ip".
-config CONFIG_FEATURE_IPCALC_FANCY
- bool " Fancy IPCALC, more options, adds 300 bytes"
+config FEATURE_IP_ROUTE
+ bool "ip route"
default y
- depends on CONFIG_IPCALC
+ depends on IP
help
- Adds the fields hostname and silent to the output of "ipcalc".
+ Add support for routing table management to "ip".
-config CONFIG_IPADDR
- bool "ipaddr"
+config FEATURE_IP_TUNNEL
+ bool "ip tunnel"
default n
+ depends on IP
help
- Equivalent to selecting address support to "ip", above.
+ Add support for tunneling commands to "ip".
-config CONFIG_IPLINK
- bool "iplink"
+config FEATURE_IP_RULE
+ bool "ip rule"
default n
+ depends on IP
help
- Equivalent to selecting link support to "ip", above.
+ Add support for rule commands to "ip".
-config CONFIG_IPROUTE
- bool "iproute"
+config FEATURE_IP_SHORT_FORMS
+ bool "Support short forms of ip commands"
default n
+ depends on IP
help
- Equivalent to selecting route support to "ip", above.
+ Also support short-form of ip <OBJECT> commands:
+ ip addr -> ipaddr
+ ip link -> iplink
+ ip route -> iproute
+ ip tunnel -> iptunnel
+ ip rule -> iprule
+
+ Say N unless you desparately need the short form of the ip
+ object commands.
-config CONFIG_IPTUNNEL
- bool "iptunnel"
+config FEATURE_IP_RARE_PROTOCOLS
+ bool "Support displaying rarely used link types"
default n
+ depends on IP
help
- Equivalent to selecting tunnel support to "ip", above.
+ If you are not going to use links of type "frad", "econet",
+ "bif" etc, you probably don't need to enable this.
+ Ethernet, wireless, infrared, ppp/slip, ip tunnelling
+ link types are supported without this option selected.
-config CONFIG_NAMEIF
+config IPADDR
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS
+
+config IPLINK
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK
+
+config IPROUTE
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE
+
+config IPTUNNEL
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL
+
+config IPRULE
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
+
+config IPCALC
+ bool "ipcalc"
+ default n
+ help
+ ipcalc takes an IP address and netmask and calculates the
+ resulting broadcast, network, and host range.
+
+config FEATURE_IPCALC_FANCY
+ bool "Fancy IPCALC, more options, adds 1 kbyte"
+ default y
+ depends on IPCALC
+ help
+ Adds the options hostname, prefix and silent to the output of
+ "ipcalc".
+
+config FEATURE_IPCALC_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on IPCALC && GETOPT_LONG
+ help
+ Support long options for the ipcalc applet.
+
+config NAMEIF
bool "nameif"
default n
+ select FEATURE_SYSLOG
help
- nameif used to rename network interface by its MAC address.
+ nameif is used to rename network interface by its MAC address.
Renamed interfaces MUST be in the down state.
- It is possible to use file (default: /etc/mactab)
+ It is possible to use a file (default: /etc/mactab)
with list of new interface names and MACs.
- Maximum interface name length: IF_NAMESIZE = 16
- File fields are sepatated by space or tab.
+ Maximum interface name length: IFNAMSIZ = 16
+ File fields are separated by space or tab.
File format:
# Comment
- new_interface_name XX:XX:XX:XX:XX:XX
+ new_interface_name XX:XX:XX:XX:XX:XX
+
+config FEATURE_NAMEIF_EXTENDED
+ bool "Extended nameif"
+ default n
+ depends on NAMEIF
+ help
+ This extends the nameif syntax to support the bus_info and driver
+ checks. The syntax is compatible to the normal nameif.
+ File format:
+ new_interface_name driver=asix bus=usb-0000:00:08.2-3
+ new_interface_name bus=usb-0000:00:08.2-3 00:80:C8:38:91:B5
+ new_interface_name mac=00:80:C8:38:91:B5
+ new_interface_name 00:80:C8:38:91:B5
-config CONFIG_NC
+config NC
bool "nc"
default n
help
A simple Unix utility which reads and writes data across network
connections.
-config CONFIG_NETSTAT
+config NC_SERVER
+ bool "Netcat server options (-l)"
+ default n
+ depends on NC
+ help
+ Allow netcat to act as a server.
+
+config NC_EXTRA
+ bool "Netcat extensions (-eiw and filename)"
+ default n
+ depends on NC
+ help
+ Add -e (support for executing the rest of the command line after
+ making or receiving a successful connection), -i (delay interval for
+ lines sent), -w (timeout for initial connection).
+
+config NETSTAT
bool "netstat"
default n
help
- Netstat prints information about the Linux networking subsystem.
+ netstat prints information about the Linux networking subsystem.
+
+config FEATURE_NETSTAT_WIDE
+ bool "Enable wide netstat output"
+ default n
+ depends on NETSTAT
+ help
+ Add support for wide columns. Useful when displaying IPv6 addresses
+ (-W option).
+
+config FEATURE_NETSTAT_PRG
+ bool "Enable PID/Program name output"
+ default n
+ depends on NETSTAT
+ help
+ Add support for -p flag to print out PID and program name.
+ +700 bytes of code.
-config CONFIG_NSLOOKUP
+config NSLOOKUP
bool "nslookup"
default n
help
- Nslookup is a tool to query Internet name servers.
+ nslookup is a tool to query Internet name servers.
-config CONFIG_PING
+config PING
bool "ping"
default n
help
- Ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
+ ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
elicit an ICMP ECHO_RESPONSE from a host or gateway.
-config CONFIG_FEATURE_FANCY_PING
- bool " Enable fancy ping output"
- default y
- depends on CONFIG_PING
- help
- Make the output from the ping applet include statistics, and at the
- same time provide full support for ICMP packets.
-
-config CONFIG_PING6
+config PING6
bool "ping6"
default n
- depends on CONFIG_FEATURE_IPV6
+ depends on FEATURE_IPV6 && PING
help
This will give you a ping that can talk IPv6.
-config CONFIG_FEATURE_FANCY_PING6
- bool " Enable fancy ping6 output"
+config FEATURE_FANCY_PING
+ bool "Enable fancy ping output"
default y
- depends on CONFIG_PING6
+ depends on PING
help
- Make the output from the ping6 applet include statistics, and at the
+ Make the output from the ping applet include statistics, and at the
same time provide full support for ICMP packets.
-config CONFIG_ROUTE
+config PSCAN
+ bool "pscan"
+ default n
+ help
+ Simple network port scanner.
+
+config ROUTE
bool "route"
default n
help
Route displays or manipulates the kernel's IP routing tables.
-config CONFIG_TELNET
+config SLATTACH
+ bool "slattach"
+ default n
+ help
+ slattach is a small utility to attach network interfaces to serial
+ lines.
+
+#config TC
+# bool "tc"
+# default n
+# help
+# show / manipulate traffic control settings
+#
+#config FEATURE_TC_INGRESS
+# def_bool n
+# depends on TC
+
+config TELNET
bool "telnet"
default n
help
Telnet is an interface to the TELNET protocol, but is also commonly
used to test other simple protocols.
-config CONFIG_FEATURE_TELNET_TTYPE
- bool " Pass TERM type to remote host"
+config FEATURE_TELNET_TTYPE
+ bool "Pass TERM type to remote host"
default y
- depends on CONFIG_TELNET
+ depends on TELNET
help
Setting this option will forward the TERM environment variable to the
- remote host you are connecting to. This is useful to make sure that
+ remote host you are connecting to. This is useful to make sure that
things like ANSI colors and other control sequences behave.
-config CONFIG_TELNETD
+config FEATURE_TELNET_AUTOLOGIN
+ bool "Pass USER type to remote host"
+ default y
+ depends on TELNET
+ help
+ Setting this option will forward the USER environment variable to the
+ remote host you are connecting to. This is useful when you need to
+ log into a machine without telling the username (autologin). This
+ option enables `-a' and `-l USER' arguments.
+
+config TELNETD
bool "telnetd"
default n
+ select FEATURE_SYSLOG
help
- A daemon for the TELNET protocol, allowing you to log on to the host
- running the daemon. Please keep in mind that the TELNET protocol
- sends passwords in plain text. If you can't afford the space for
- any SSH daemon and you trust your network, say 'y' here.
+ A daemon for the TELNET protocol, allowing you to log onto the host
+ running the daemon. Please keep in mind that the TELNET protocol
+ sends passwords in plain text. If you can't afford the space for an
+ SSH daemon and you trust your network, you may say 'y' here. As a
+ more secure alternative, you should seriously consider installing the
+ very small Dropbear SSH daemon instead:
+ http://matt.ucc.asn.au/dropbear/dropbear.html
+
+ Note that for busybox telnetd to work you need several things:
+ First of all, your kernel needs:
+ UNIX98_PTYS=y
+ DEVPTS_FS=y
+
+ Next, you need a /dev/pts directory on your root filesystem:
+
+ $ ls -ld /dev/pts
+ drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
+
+ Next you need the pseudo terminal master multiplexer /dev/ptmx:
+
+ $ ls -la /dev/ptmx
+ crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
+
+ Any /dev/ttyp[0-9]* files you may have can be removed.
+ Next, you need to mount the devpts filesystem on /dev/pts using:
+
+ mount -t devpts devpts /dev/pts
+
+ You need to be sure that Busybox has LOGIN and
+ FEATURE_SUID enabled. And finally, you should make
+ certain that Busybox has been installed setuid root:
-config CONFIG_FEATURE_TELNETD_INETD
- bool " Support call from inetd only"
+ chown root.root /bin/busybox
+ chmod 4755 /bin/busybox
+
+ with all that done, telnetd _should_ work....
+
+
+config FEATURE_TELNETD_STANDALONE
+ bool "Support standalone telnetd (not inetd only)"
default n
- depends on CONFIG_TELNETD
+ depends on TELNETD
help
- Selecting this will make telnetd only callable from inetd, removing the
- standalone support.
+ Selecting this will make telnetd able to run standalone.
-config CONFIG_TFTP
+config TFTP
bool "tftp"
default n
help
- This enables the Tirvial File Transfer Protocol client program. TFTP
+ This enables the Trivial File Transfer Protocol client program. TFTP
is usually used for simple, small transfers such as a root image
for a network-enabled bootloader.
-config CONFIG_FEATURE_TFTP_GET
- bool " Enable \"get\" command"
+config TFTPD
+ bool "tftpd"
+ default n
+ help
+ This enables the Trivial File Transfer Protocol server program.
+ It expects that stdin is a datagram socket and a packet
+ is already pending on it. It will exit after one transfer.
+ In other words: it should be run from inetd in nowait mode,
+ or from udpsvd. Example: "udpsvd -E 0 69 tftpd DIR"
+
+config FEATURE_TFTP_GET
+ bool "Enable \"get\" command"
default y
- depends on CONFIG_TFTP
+ depends on TFTP || TFTPD
help
- Add support for the GET command within the TFTP client. This allows
- a client to retreive a file from a TFTP server.
+ Add support for the GET command within the TFTP client. This allows
+ a client to retrieve a file from a TFTP server.
+ Also enable upload support in tftpd, if tftpd is selected.
-config CONFIG_FEATURE_TFTP_PUT
- bool " Enable \"put\" command"
+config FEATURE_TFTP_PUT
+ bool "Enable \"put\" command"
default y
- depends on CONFIG_TFTP
+ depends on TFTP || TFTPD
help
- Add support for the PUT command within the TFTP client. This allows
+ Add support for the PUT command within the TFTP client. This allows
a client to transfer a file to a TFTP server.
+ Also enable download support in tftpd, if tftpd is selected.
-config CONFIG_FEATURE_TFTP_BLOCKSIZE
- bool " Enable \"blocksize\" command"
+config FEATURE_TFTP_BLOCKSIZE
+ bool "Enable \"blksize\" protocol option"
default n
- depends on CONFIG_TFTP
+ depends on TFTP || TFTPD
help
- Allow the client to specify the desired block size for transfers.
+ Allow tftp to specify block size, and tftpd to understand
+ "blksize" option.
-config CONFIG_FEATURE_TFTP_DEBUG
- bool " Enable debug"
+config TFTP_DEBUG
+ bool "Enable debug"
default n
- depends on CONFIG_TFTP
+ depends on TFTP || TFTPD
help
- Enable debug settings for tftp. This is useful if you're running
+ Enable debug settings for tftp. This is useful if you're running
into problems with tftp as the protocol doesn't help you much when
you run into problems.
-config CONFIG_TRACEROUTE
+config TRACEROUTE
bool "traceroute"
default n
help
Utility to trace the route of IP packets
-config CONFIG_FEATURE_TRACEROUTE_VERBOSE
- bool " Enable verbose output"
+config FEATURE_TRACEROUTE_VERBOSE
+ bool "Enable verbose output"
default n
- depends on CONFIG_TRACEROUTE
+ depends on TRACEROUTE
help
- Add some verbosity to traceroute. This includes amongst other things
+ Add some verbosity to traceroute. This includes among other things
hostnames and ICMP response types.
-config CONFIG_VCONFIG
+config FEATURE_TRACEROUTE_SOURCE_ROUTE
+ bool "Enable loose source route"
+ default n
+ depends on TRACEROUTE
+ help
+ Add option to specify a loose source route gateway
+ (8 maximum).
+
+config FEATURE_TRACEROUTE_USE_ICMP
+ bool "Use ICMP instead of UDP"
+ default n
+ depends on TRACEROUTE
+ help
+ Add option -I to use ICMP ECHO instead of UDP datagrams.
+
+source networking/udhcp/Config.in
+
+config IFUPDOWN_UDHCPC_CMD_OPTIONS
+ string "ifup udhcpc command line options"
+ default "-R -n"
+ depends on IFUPDOWN && APP_UDHCPC
+ help
+ Command line options to pass to udhcpc from ifup.
+ Intended to alter options not available in /etc/network/interfaces.
+ (IE: --syslog --background etc...)
+
+config VCONFIG
bool "vconfig"
default n
help
Creates, removes, and configures VLAN interfaces
-config CONFIG_WGET
+config WGET
bool "wget"
default n
help
- Wget is a utility for non-interactive download of files from HTTP,
+ wget is a utility for non-interactive download of files from HTTP,
HTTPS, and FTP servers.
-config CONFIG_FEATURE_WGET_STATUSBAR
- bool " Enable a nifty process meter (+2k)"
+config FEATURE_WGET_STATUSBAR
+ bool "Enable a nifty process meter (+2k)"
default y
- depends on CONFIG_WGET
+ depends on WGET
help
Enable the transfer progress bar for wget transfers.
-config CONFIG_FEATURE_WGET_AUTHENTICATION
- bool " Enable HTTP authentication"
+config FEATURE_WGET_AUTHENTICATION
+ bool "Enable HTTP authentication"
default y
- depends on CONFIG_WGET
+ depends on WGET
help
Support authenticated HTTP transfers.
-source networking/udhcp/Config.in
+config FEATURE_WGET_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on WGET && GETOPT_LONG
+ help
+ Support long options for the wget applet.
-endmenu
+config ZCIP
+ bool "zcip"
+ default n
+ select FEATURE_SYSLOG
+ help
+ ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
+ It's a daemon that allocates and defends a dynamically assigned
+ address on the 169.254/16 network, requiring no system administrator.
+
+ See http://www.zeroconf.org for further details, and "zcip.script"
+ in the busybox examples.
+
+config TCPSVD
+ bool "tcpsvd"
+ default n
+ help
+ tcpsvd listens on a TCP port and runs a program for each new
+ connection.
+
+config TUNCTL
+ bool "tunctl"
+ default n
+ help
+ tunctl creates or deletes tun devices.
+
+config FEATURE_TUNCTL_UG
+ bool "Support owner:group assignment"
+ default n
+ depends on TUNCTL
+ help
+ Allow to specify owner and group of newly created interface.
+ 340 bytes of pure bloat. Say no here.
+
+config UDPSVD
+ bool "udpsvd"
+ default n
+ help
+ udpsvd listens on an UDP port and runs a program for each new
+ connection.
+endmenu
diff --git a/release/src/router/busybox/networking/Kbuild b/release/src/router/busybox/networking/Kbuild
new file mode 100644
index 00000000..d632774f
--- /dev/null
+++ b/release/src/router/busybox/networking/Kbuild
@@ -0,0 +1,46 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ARP) += arp.o interface.o
+lib-$(CONFIG_ARPING) += arping.o
+lib-$(CONFIG_BRCTL) += brctl.o
+lib-$(CONFIG_DNSD) += dnsd.o
+lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
+lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
+lib-$(CONFIG_FTPD) += ftpd.o
+lib-$(CONFIG_FTPGET) += ftpgetput.o
+lib-$(CONFIG_FTPPUT) += ftpgetput.o
+lib-$(CONFIG_HOSTNAME) += hostname.o
+lib-$(CONFIG_HTTPD) += httpd.o
+lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
+lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
+lib-$(CONFIG_IFUPDOWN) += ifupdown.o
+lib-$(CONFIG_INETD) += inetd.o
+lib-$(CONFIG_IP) += ip.o
+lib-$(CONFIG_IPCALC) += ipcalc.o
+lib-$(CONFIG_NAMEIF) += nameif.o
+lib-$(CONFIG_NC) += nc.o
+lib-$(CONFIG_NETSTAT) += netstat.o
+lib-$(CONFIG_NSLOOKUP) += nslookup.o
+lib-$(CONFIG_PING) += ping.o
+lib-$(CONFIG_PING6) += ping.o
+lib-$(CONFIG_PSCAN) += pscan.o
+lib-$(CONFIG_ROUTE) += route.o
+lib-$(CONFIG_SLATTACH) += slattach.o
+lib-$(CONFIG_TC) += tc.o
+lib-$(CONFIG_TELNET) += telnet.o
+lib-$(CONFIG_TELNETD) += telnetd.o
+lib-$(CONFIG_TFTP) += tftp.o
+lib-$(CONFIG_TFTPD) += tftp.o
+lib-$(CONFIG_TRACEROUTE) += traceroute.o
+lib-$(CONFIG_TUNCTL) += tunctl.o
+lib-$(CONFIG_VCONFIG) += vconfig.o
+lib-$(CONFIG_WGET) += wget.o
+lib-$(CONFIG_ZCIP) += zcip.o
+
+lib-$(CONFIG_TCPSVD) += tcpudp.o tcpudp_perhost.o
+lib-$(CONFIG_UDPSVD) += tcpudp.o tcpudp_perhost.o
diff --git a/release/src/router/busybox/networking/Makefile b/release/src/router/busybox/networking/Makefile
deleted file mode 100644
index 022f29e4..00000000
--- a/release/src/router/busybox/networking/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-TOPDIR:= ../
-NETWORKING_DIR:=./
-include $(TOPDIR).config
-include $(TOPDIR)Rules.mak
-include Makefile.in
-all: $(libraries-y)
--include $(TOPDIR).depend
-
-clean:
- rm -f *.o *.a $(AR_TARGET)
-
diff --git a/release/src/router/busybox/networking/Makefile.in b/release/src/router/busybox/networking/Makefile.in
deleted file mode 100755
index 7748d066..00000000
--- a/release/src/router/busybox/networking/Makefile.in
+++ /dev/null
@@ -1,65 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-NETWORKING_AR:=networking.a
-ifndef $(NETWORKING_DIR)
-NETWORKING_DIR:=$(TOPDIR)networking/
-endif
-
-NETWORKING-y:=
-NETWORKING-$(CONFIG_ARPING) += arping.o
-NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o
-NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o
-NETWORKING-$(CONFIG_HOSTNAME) += hostname.o
-NETWORKING-$(CONFIG_HTTPD) += httpd.o
-NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o
-NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o
-NETWORKING-$(CONFIG_INETD) += inetd.o
-NETWORKING-$(CONFIG_IP) += ip.o
-NETWORKING-$(CONFIG_IPCALC) += ipcalc.o
-NETWORKING-$(CONFIG_IPADDR) += ipaddr.o
-NETWORKING-$(CONFIG_IPLINK) += iplink.o
-NETWORKING-$(CONFIG_IPROUTE) += iproute.o
-NETWORKING-$(CONFIG_IPTUNNEL) += iptunnel.o
-NETWORKING-$(CONFIG_NAMEIF) += nameif.o
-NETWORKING-$(CONFIG_NC) += nc.o
-NETWORKING-$(CONFIG_NETSTAT) += netstat.o
-NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o
-NETWORKING-$(CONFIG_PING) += ping.o
-NETWORKING-$(CONFIG_PING6) += ping6.o
-NETWORKING-$(CONFIG_ROUTE) += route.o
-NETWORKING-$(CONFIG_TELNET) += telnet.o
-NETWORKING-$(CONFIG_TELNETD) += telnetd.o
-NETWORKING-$(CONFIG_TFTP) += tftp.o
-NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o
-NETWORKING-$(CONFIG_VCONFIG) += vconfig.o
-NETWORKING-$(CONFIG_WGET) += wget.o
-
-libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR)
-
-needcrypt-y:=
-needcrypt-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) := y
-
-ifeq ($(needcrypt-y),y)
- LIBRARIES += -lcrypt
-endif
-
-$(NETWORKING_DIR)$(NETWORKING_AR): $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
- $(AR) -ro $@ $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
-
diff --git a/release/src/router/busybox/networking/arp.c b/release/src/router/busybox/networking/arp.c
new file mode 100644
index 00000000..278f2dc9
--- /dev/null
+++ b/release/src/router/busybox/networking/arp.c
@@ -0,0 +1,515 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arp.c - Manipulate the system ARP cache
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Fred N. van Kempen, <waltje at uwalt.nl.mugnet.org>
+ * Busybox port: Paul van Gool <pvangool at mimotech.com>
+ *
+ * modified for getopt32 by Arne Bernin <arne [at] alamut.de>
+ */
+
+#include "libbb.h"
+#include "inet_common.h"
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#define DEBUG 0
+
+#define DFLT_AF "inet"
+#define DFLT_HW "ether"
+
+enum {
+ ARP_OPT_A = (1 << 0),
+ ARP_OPT_p = (1 << 1),
+ ARP_OPT_H = (1 << 2),
+ ARP_OPT_t = (1 << 3),
+ ARP_OPT_i = (1 << 4),
+ ARP_OPT_a = (1 << 5),
+ ARP_OPT_d = (1 << 6),
+ ARP_OPT_n = (1 << 7), /* do not resolve addresses */
+ ARP_OPT_D = (1 << 8), /* HW-address is devicename */
+ ARP_OPT_s = (1 << 9),
+ ARP_OPT_v = (1 << 10) * DEBUG, /* debugging output flag */
+};
+
+enum {
+ sockfd = 3, /* active socket descriptor */
+};
+
+struct globals {
+ const struct aftype *ap; /* current address family */
+ const struct hwtype *hw; /* current hardware type */
+ const char *device; /* current device */
+ smallint hw_set; /* flag if hw-type was set (-H) */
+
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define ap (G.ap )
+#define hw (G.hw )
+#define device (G.device )
+#define hw_set (G.hw_set )
+#define INIT_G() do { \
+ device = ""; \
+} while (0)
+
+
+static const char options[] ALIGN1 =
+ "pub\0"
+ "priv\0"
+ "temp\0"
+ "trail\0"
+ "dontpub\0"
+ "auto\0"
+ "dev\0"
+ "netmask\0";
+
+/* Delete an entry from the ARP cache. */
+/* Called only from main, once */
+static int arp_del(char **args)
+{
+ char *host;
+ struct arpreq req;
+ struct sockaddr sa;
+ int flags = 0;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+
+ /* Resolve the host name. */
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+
+ /* If a host has more than one address, use the correct one! */
+ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+ if (hw_set)
+ req.arp_ha.sa_family = hw->type;
+
+ req.arp_flags = ATF_PERM;
+ args++;
+ while (*args != NULL) {
+ switch (index_in_strings(options, *args)) {
+ case 0: /* "pub" */
+ flags |= 1;
+ args++;
+ break;
+ case 1: /* "priv" */
+ flags |= 2;
+ args++;
+ break;
+ case 2: /* "temp" */
+ req.arp_flags &= ~ATF_PERM;
+ args++;
+ break;
+ case 3: /* "trail" */
+ req.arp_flags |= ATF_USETRAILERS;
+ args++;
+ break;
+ case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+ req.arp_flags |= ATF_DONTPUB;
+#else
+ bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+ args++;
+ break;
+ case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+ req.arp_flags |= ATF_MAGIC;
+#else
+ bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+ args++;
+ break;
+ case 6: /* "dev" */
+ if (*++args == NULL)
+ bb_show_usage();
+ device = *args;
+ args++;
+ break;
+ case 7: /* "netmask" */
+ if (*++args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "255.255.255.255") != 0) {
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+ req.arp_flags |= ATF_NETMASK;
+ }
+ args++;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+ if (flags == 0)
+ flags = 3;
+
+ strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+ err = -1;
+
+ /* Call the kernel. */
+ if (flags & 2) {
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCDARP(nopub)");
+ err = ioctl(sockfd, SIOCDARP, &req);
+ if (err < 0) {
+ if (errno == ENXIO) {
+ if (flags & 1)
+ goto nopub;
+ printf("No ARP entry for %s\n", host);
+ return -1;
+ }
+ bb_perror_msg_and_die("SIOCDARP(priv)");
+ }
+ }
+ if ((flags & 1) && err) {
+ nopub:
+ req.arp_flags |= ATF_PUBL;
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCDARP(pub)");
+ if (ioctl(sockfd, SIOCDARP, &req) < 0) {
+ if (errno == ENXIO) {
+ printf("No ARP entry for %s\n", host);
+ return -1;
+ }
+ bb_perror_msg_and_die("SIOCDARP(pub)");
+ }
+ }
+ return 0;
+}
+
+/* Get the hardware address to a specified interface name */
+static void arp_getdevhw(char *ifname, struct sockaddr *sa,
+ const struct hwtype *hwt)
+{
+ struct ifreq ifr;
+ const struct hwtype *xhw;
+
+ strcpy(ifr.ifr_name, ifname);
+ ioctl_or_perror_and_die(sockfd, SIOCGIFHWADDR, &ifr,
+ "cant get HW-Address for '%s'", ifname);
+ if (hwt && (ifr.ifr_hwaddr.sa_family != hw->type)) {
+ bb_error_msg_and_die("protocol type mismatch");
+ }
+ memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
+
+ if (option_mask32 & ARP_OPT_v) {
+ xhw = get_hwntype(ifr.ifr_hwaddr.sa_family);
+ if (!xhw || !xhw->print) {
+ xhw = get_hwntype(-1);
+ }
+ bb_error_msg("device '%s' has HW address %s '%s'",
+ ifname, xhw->name,
+ xhw->print((unsigned char *) &ifr.ifr_hwaddr.sa_data));
+ }
+}
+
+/* Set an entry in the ARP cache. */
+/* Called only from main, once */
+static int arp_set(char **args)
+{
+ char *host;
+ struct arpreq req;
+ struct sockaddr sa;
+ int flags;
+
+ memset(&req, 0, sizeof(req));
+
+ host = *args++;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ /* If a host has more than one address, use the correct one! */
+ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+ /* Fetch the hardware address. */
+ if (*args == NULL) {
+ bb_error_msg_and_die("need hardware address");
+ }
+ if (option_mask32 & ARP_OPT_D) {
+ arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL);
+ } else {
+ if (hw->input(*args++, &req.arp_ha) < 0) {
+ bb_error_msg_and_die("invalid hardware address");
+ }
+ }
+
+ /* Check out any modifiers. */
+ flags = ATF_PERM | ATF_COM;
+ while (*args != NULL) {
+ switch (index_in_strings(options, *args)) {
+ case 0: /* "pub" */
+ flags |= ATF_PUBL;
+ args++;
+ break;
+ case 1: /* "priv" */
+ flags &= ~ATF_PUBL;
+ args++;
+ break;
+ case 2: /* "temp" */
+ flags &= ~ATF_PERM;
+ args++;
+ break;
+ case 3: /* "trail" */
+ flags |= ATF_USETRAILERS;
+ args++;
+ break;
+ case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+ flags |= ATF_DONTPUB;
+#else
+ bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+ args++;
+ break;
+ case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+ flags |= ATF_MAGIC;
+#else
+ bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+ args++;
+ break;
+ case 6: /* "dev" */
+ if (*++args == NULL)
+ bb_show_usage();
+ device = *args;
+ args++;
+ break;
+ case 7: /* "netmask" */
+ if (*++args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "255.255.255.255") != 0) {
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+ flags |= ATF_NETMASK;
+ }
+ args++;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+
+ /* Fill in the remainder of the request. */
+ req.arp_flags = flags;
+
+ strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+ /* Call the kernel. */
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCSARP()");
+ xioctl(sockfd, SIOCSARP, &req);
+ return 0;
+}
+
+
+/* Print the contents of an ARP request block. */
+static void
+arp_disp(const char *name, char *ip, int type, int arp_flags,
+ char *hwa, char *mask, char *dev)
+{
+ static const int arp_masks[] = {
+ ATF_PERM, ATF_PUBL,
+#ifdef HAVE_ATF_MAGIC
+ ATF_MAGIC,
+#endif
+#ifdef HAVE_ATF_DONTPUB
+ ATF_DONTPUB,
+#endif
+ ATF_USETRAILERS,
+ };
+ static const char arp_labels[] ALIGN1 = "PERM\0""PUP\0"
+#ifdef HAVE_ATF_MAGIC
+ "AUTO\0"
+#endif
+#ifdef HAVE_ATF_DONTPUB
+ "DONTPUB\0"
+#endif
+ "TRAIL\0"
+ ;
+
+ const struct hwtype *xhw;
+
+ xhw = get_hwntype(type);
+ if (xhw == NULL)
+ xhw = get_hwtype(DFLT_HW);
+
+ printf("%s (%s) at ", name, ip);
+
+ if (!(arp_flags & ATF_COM)) {
+ if (arp_flags & ATF_PUBL)
+ printf("* ");
+ else
+ printf("<incomplete> ");
+ } else {
+ printf("%s [%s] ", hwa, xhw->name);
+ }
+
+ if (arp_flags & ATF_NETMASK)
+ printf("netmask %s ", mask);
+
+ print_flags_separated(arp_masks, arp_labels, arp_flags, " ");
+ printf(" on %s\n", dev);
+}
+
+/* Display the contents of the ARP cache in the kernel. */
+/* Called only from main, once */
+static int arp_show(char *name)
+{
+ const char *host;
+ const char *hostname;
+ FILE *fp;
+ struct sockaddr sa;
+ int type, flags;
+ int num;
+ unsigned entries = 0, shown = 0;
+ char ip[128];
+ char hwa[128];
+ char mask[128];
+ char line[128];
+ char dev[128];
+
+ host = NULL;
+ if (name != NULL) {
+ /* Resolve the host name. */
+ if (ap->input(name, &sa) < 0) {
+ bb_herror_msg_and_die("%s", name);
+ }
+ host = xstrdup(ap->sprint(&sa, 1));
+ }
+ fp = xfopen_for_read("/proc/net/arp");
+ /* Bypass header -- read one line */
+ fgets(line, sizeof(line), fp);
+
+ /* Read the ARP cache entries. */
+ while (fgets(line, sizeof(line), fp)) {
+
+ mask[0] = '-'; mask[1] = '\0';
+ dev[0] = '-'; dev[1] = '\0';
+ /* All these strings can't overflow
+ * because fgets above reads limited amount of data */
+ num = sscanf(line, "%s 0x%x 0x%x %s %s %s\n",
+ ip, &type, &flags, hwa, mask, dev);
+ if (num < 4)
+ break;
+
+ entries++;
+ /* if the user specified hw-type differs, skip it */
+ if (hw_set && (type != hw->type))
+ continue;
+
+ /* if the user specified address differs, skip it */
+ if (host && strcmp(ip, host) != 0)
+ continue;
+
+ /* if the user specified device differs, skip it */
+ if (device[0] && strcmp(dev, device) != 0)
+ continue;
+
+ shown++;
+ /* This IS ugly but it works -be */
+ hostname = "?";
+ if (!(option_mask32 & ARP_OPT_n)) {
+ if (ap->input(ip, &sa) < 0)
+ hostname = ip;
+ else
+ hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000);
+ if (strcmp(hostname, ip) == 0)
+ hostname = "?";
+ }
+
+ arp_disp(hostname, ip, type, flags, hwa, mask, dev);
+ }
+ if (option_mask32 & ARP_OPT_v)
+ printf("Entries: %d\tSkipped: %d\tFound: %d\n",
+ entries, entries - shown, shown);
+
+ if (!shown) {
+ if (hw_set || host || device[0])
+ printf("No match found in %d entries\n", entries);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free((char*)host);
+ fclose(fp);
+ }
+ return 0;
+}
+
+int arp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int arp_main(int argc UNUSED_PARAM, char **argv)
+{
+ const char *hw_type = "ether";
+ const char *protocol;
+ unsigned opts;
+
+ INIT_G();
+
+ xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sockfd);
+ ap = get_aftype(DFLT_AF);
+ if (!ap)
+ bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
+
+ opts = getopt32(argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
+ &hw_type, &hw_type, &device);
+ argv += optind;
+ if (opts & (ARP_OPT_A | ARP_OPT_p)) {
+ ap = get_aftype(protocol);
+ if (ap == NULL)
+ bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
+ }
+ if (opts & (ARP_OPT_A | ARP_OPT_p)) {
+ hw = get_hwtype(hw_type);
+ if (hw == NULL)
+ bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
+ hw_set = 1;
+ }
+ //if (opts & ARP_OPT_i)... -i
+
+ if (ap->af != AF_INET) {
+ bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
+ }
+
+ /* If no hw type specified get default */
+ if (!hw) {
+ hw = get_hwtype(DFLT_HW);
+ if (!hw)
+ bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
+ }
+
+ if (hw->alen <= 0) {
+ bb_error_msg_and_die("%s: %s without ARP support",
+ hw->name, "hardware type");
+ }
+
+ /* Now see what we have to do here... */
+ if (opts & (ARP_OPT_d | ARP_OPT_s)) {
+ if (argv[0] == NULL)
+ bb_error_msg_and_die("need host name");
+ if (opts & ARP_OPT_s)
+ return arp_set(argv);
+ return arp_del(argv);
+ }
+ //if (opts & ARP_OPT_a) - default
+ return arp_show(argv[0]);
+}
diff --git a/release/src/router/busybox/networking/arping.c b/release/src/router/busybox/networking/arping.c
index 2e1adf0a..915af326 100644
--- a/release/src/router/busybox/networking/arping.c
+++ b/release/src/router/busybox/networking/arping.c
@@ -1,199 +1,199 @@
+/* vi: set sw=4 ts=4: */
/*
* arping.c - Ping hosts by ARP requests/replies
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
* Busybox port: Nick Fedchik <nick@fedchik.org.ua>
*/
-#include <sys/ioctl.h>
-#include <sys/signal.h>
-#include <sys/time.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
-#include "busybox.h"
-
-#define APPLET_NAME "arping"
-
-struct in_addr src;
-struct in_addr dst;
-struct sockaddr_ll me;
-struct sockaddr_ll he;
-struct timeval last;
-int dad;
-int unsolicited;
-int advert;
-int quiet;
-int quit_on_reply = 0;
-int count = -1;
-int timeout;
-int unicasting;
-int s;
-int broadcast_only;
-int sent;
-int brd_sent;
-int received;
-int brd_recv;
-int req_recv;
-
-#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
- ((tv1).tv_usec-(tv2).tv_usec)/1000 )
-#if 0
-static void set_signal(int signo, void (*handler) (void))
-{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = (void (*)(int)) handler;
- sa.sa_flags = SA_RESTART;
- sigaction(signo, &sa, NULL);
-}
-#endif
-
-static int send_pack(int sock, struct in_addr *src_addr,
- struct in_addr *dst_addr, struct sockaddr_ll *ME,
- struct sockaddr_ll *HE)
+#include "libbb.h"
+
+/* We don't expect to see 1000+ seconds delay, unsigned is enough */
+#define MONOTONIC_US() ((unsigned)monotonic_us())
+
+enum {
+ DAD = 1,
+ UNSOLICITED = 2,
+ ADVERT = 4,
+ QUIET = 8,
+ QUIT_ON_REPLY = 16,
+ BCAST_ONLY = 32,
+ UNICASTING = 64
+};
+
+struct globals {
+ struct in_addr src;
+ struct in_addr dst;
+ struct sockaddr_ll me;
+ struct sockaddr_ll he;
+ int sock_fd;
+
+ int count; // = -1;
+ unsigned last;
+ unsigned timeout_us;
+ unsigned start;
+
+ unsigned sent;
+ unsigned brd_sent;
+ unsigned received;
+ unsigned brd_recv;
+ unsigned req_recv;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define src (G.src )
+#define dst (G.dst )
+#define me (G.me )
+#define he (G.he )
+#define sock_fd (G.sock_fd )
+#define count (G.count )
+#define last (G.last )
+#define timeout_us (G.timeout_us)
+#define start (G.start )
+#define sent (G.sent )
+#define brd_sent (G.brd_sent )
+#define received (G.received )
+#define brd_recv (G.brd_recv )
+#define req_recv (G.req_recv )
+#define INIT_G() do { \
+ count = -1; \
+} while (0)
+
+// If GNUisms are not available...
+//static void *mempcpy(void *_dst, const void *_src, int n)
+//{
+// memcpy(_dst, _src, n);
+// return (char*)_dst + n;
+//}
+
+static int send_pack(struct in_addr *src_addr,
+ struct in_addr *dst_addr, struct sockaddr_ll *ME,
+ struct sockaddr_ll *HE)
{
int err;
- struct timeval now;
unsigned char buf[256];
struct arphdr *ah = (struct arphdr *) buf;
unsigned char *p = (unsigned char *) (ah + 1);
- ah->ar_hrd = htons(ME->sll_hatype);
ah->ar_hrd = htons(ARPHRD_ETHER);
ah->ar_pro = htons(ETH_P_IP);
ah->ar_hln = ME->sll_halen;
ah->ar_pln = 4;
- ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
-
- memcpy(p, &ME->sll_addr, ah->ar_hln);
- p += ME->sll_halen;
+ ah->ar_op = option_mask32 & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
- memcpy(p, src_addr, 4);
- p += 4;
+ p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
+ p = mempcpy(p, src_addr, 4);
- if (advert)
- memcpy(p, &ME->sll_addr, ah->ar_hln);
+ if (option_mask32 & ADVERT)
+ p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
else
- memcpy(p, &HE->sll_addr, ah->ar_hln);
- p += ah->ar_hln;
+ p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
- memcpy(p, dst_addr, 4);
- p += 4;
+ p = mempcpy(p, dst_addr, 4);
- gettimeofday(&now, NULL);
- err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
+ err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
if (err == p - buf) {
- last = now;
+ last = MONOTONIC_US();
sent++;
- if (!unicasting)
+ if (!(option_mask32 & UNICASTING))
brd_sent++;
}
return err;
}
-void finish(void)
+static void finish(void) NORETURN;
+static void finish(void)
{
- if (!quiet) {
- printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
- printf("Received %d repl%s", received, (received > 1) ? "ies" : "y");
- if (brd_recv || req_recv) {
- printf(" (");
- if (req_recv)
- printf("%d request(s)", req_recv);
- if (brd_recv)
- printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv);
- putchar(')');
- }
- putchar('\n');
- fflush(stdout);
+ if (!(option_mask32 & QUIET)) {
+ printf("Sent %u probe(s) (%u broadcast(s))\n"
+ "Received %u repl%s"
+ " (%u request(s), %u broadcast(s))\n",
+ sent, brd_sent,
+ received, (received == 1) ? "ies" : "y",
+ req_recv, brd_recv);
}
- if (dad)
+ if (option_mask32 & DAD)
exit(!!received);
- if (unsolicited)
- exit(0);
+ if (option_mask32 & UNSOLICITED)
+ exit(EXIT_SUCCESS);
exit(!received);
}
-void catcher(void)
+static void catcher(void)
{
- struct timeval tv;
- struct timeval start;
-
- gettimeofday(&tv, NULL);
+ unsigned now;
- if (start.tv_sec == 0)
- start = tv;
+ now = MONOTONIC_US();
+ if (start == 0)
+ start = now;
- if (count-- == 0
- || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500))
+ if (count == 0 || (timeout_us && (now - start) > timeout_us))
finish();
- if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) {
- send_pack(s, &src, &dst, &me, &he);
- if (count == 0 && unsolicited)
+ /* count < 0 means "infinite count" */
+ if (count > 0)
+ count--;
+
+ if (last == 0 || (now - last) > 500000) {
+ send_pack(&src, &dst, &me, &he);
+ if (count == 0 && (option_mask32 & UNSOLICITED))
finish();
}
alarm(1);
}
-int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
{
- struct timeval tv;
struct arphdr *ah = (struct arphdr *) buf;
unsigned char *p = (unsigned char *) (ah + 1);
struct in_addr src_ip, dst_ip;
-
- gettimeofday(&tv, NULL);
+ /* moves below assume in_addr is 4 bytes big, ensure that */
+ struct BUG_in_addr_must_be_4 {
+ char BUG_in_addr_must_be_4[
+ sizeof(struct in_addr) == 4 ? 1 : -1
+ ];
+ char BUG_s_addr_must_be_4[
+ sizeof(src_ip.s_addr) == 4 ? 1 : -1
+ ];
+ };
/* Filter out wild packets */
- if (FROM->sll_pkttype != PACKET_HOST &&
- FROM->sll_pkttype != PACKET_BROADCAST &&
- FROM->sll_pkttype != PACKET_MULTICAST)
- return 0;
+ if (FROM->sll_pkttype != PACKET_HOST
+ && FROM->sll_pkttype != PACKET_BROADCAST
+ && FROM->sll_pkttype != PACKET_MULTICAST)
+ return false;
/* Only these types are recognised */
if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
- return 0;
+ return false;
/* ARPHRD check and this darned FDDI hack here :-( */
- if (ah->ar_hrd != htons(FROM->sll_hatype) &&
- (FROM->sll_hatype != ARPHRD_FDDI
- || ah->ar_hrd != htons(ARPHRD_ETHER)))
- return 0;
+ if (ah->ar_hrd != htons(FROM->sll_hatype)
+ && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
+ return false;
/* Protocol must be IP. */
- if (ah->ar_pro != htons(ETH_P_IP))
- return 0;
- if (ah->ar_pln != 4)
- return 0;
- if (ah->ar_hln != me.sll_halen)
- return 0;
- if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln))
- return 0;
- memcpy(&src_ip, p + ah->ar_hln, 4);
- memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
- if (!dad) {
- if (src_ip.s_addr != dst.s_addr)
- return 0;
- if (src.s_addr != dst_ip.s_addr)
- return 0;
- if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))
- return 0;
+ if (ah->ar_pro != htons(ETH_P_IP)
+ || (ah->ar_pln != 4)
+ || (ah->ar_hln != me.sll_halen)
+ || (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
+ return false;
+
+ move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
+ move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
+
+ if (dst.s_addr != src_ip.s_addr)
+ return false;
+ if (!(option_mask32 & DAD)) {
+ if ((src.s_addr != dst_ip.s_addr)
+ || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
+ return false;
} else {
/* DAD packet was:
src_ip = 0 (or some src)
@@ -208,22 +208,18 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
also that it matches to dst_ip, otherwise
dst_ip/dst_hw do not matter.
*/
- if (src_ip.s_addr != dst.s_addr)
- return 0;
- if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
- return 0;
- if (src.s_addr && src.s_addr != dst_ip.s_addr)
- return 0;
+ if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
+ || (src.s_addr && src.s_addr != dst_ip.s_addr))
+ return false;
}
- if (!quiet) {
+ if (!(option_mask32 & QUIET)) {
int s_printed = 0;
- printf("%s ",
- FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast");
- printf("%s from ",
- ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
- printf("%s ", inet_ntoa(src_ip));
- printf("[%s]", ether_ntoa((struct ether_addr *) p));
+ printf("%scast re%s from %s [%s]",
+ FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
+ ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
+ inet_ntoa(src_ip),
+ ether_ntoa((struct ether_addr *) p));
if (dst_ip.s_addr != src.s_addr) {
printf("for %s ", inet_ntoa(dst_ip));
s_printed = 1;
@@ -232,15 +228,12 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
if (!s_printed)
printf("for ");
printf("[%s]",
- ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
+ ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
}
- if (last.tv_sec) {
- long usecs = (tv.tv_sec - last.tv_sec) * 1000000 +
- tv.tv_usec - last.tv_usec;
- long msecs = (usecs + 500) / 1000;
- usecs -= msecs * 1000 - 500;
- printf(" %ld.%03ldms\n", msecs, usecs);
+ if (last) {
+ unsigned diff = MONOTONIC_US() - last;
+ printf(" %u.%03ums\n", diff / 1000, diff % 1000);
} else {
printf(" UNSOLICITED?\n");
}
@@ -251,242 +244,160 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
brd_recv++;
if (ah->ar_op == htons(ARPOP_REQUEST))
req_recv++;
- if (quit_on_reply)
+ if (option_mask32 & QUIT_ON_REPLY)
finish();
- if (!broadcast_only) {
+ if (!(option_mask32 & BCAST_ONLY)) {
memcpy(he.sll_addr, p, me.sll_halen);
- unicasting = 1;
+ option_mask32 |= UNICASTING;
}
- return 1;
+ return true;
}
-int arping_main(int argc, char **argv)
+int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int arping_main(int argc UNUSED_PARAM, char **argv)
{
- int socket_errno;
- int ch;
- uid_t uid = getuid();
- char *device = "eth0";
- int ifindex = 0;
+ const char *device = "eth0";
char *source = NULL;
char *target;
+ unsigned char *packet;
+ char *err_str;
- s = socket(PF_PACKET, SOCK_DGRAM, 0);
- socket_errno = errno;
-
- setuid(uid);
-
- while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) {
- switch (ch) {
- case 'b':
- broadcast_only = 1;
- break;
- case 'D':
- dad++;
- quit_on_reply = 1;
- break;
- case 'U':
- unsolicited++;
- break;
- case 'A':
- advert++;
- unsolicited++;
- break;
- case 'q':
- quiet++;
- break;
- case 'c':
- count = atoi(optarg);
- break;
- case 'w':
- timeout = atoi(optarg);
- break;
- case 'I':
- if (optarg == NULL)
- bb_show_usage();
- if (bb_strlen(optarg) > IF_NAMESIZE) {
- bb_error_msg("Interface name `%s' must be less than %d", optarg,
- IF_NAMESIZE);
- exit(2);
- }
- device = optarg;
- break;
- case 'f':
- quit_on_reply = 1;
- break;
- case 's':
- source = optarg;
- break;
- case 'h':
- case '?':
- default:
- bb_show_usage();
- }
- }
- argc -= optind;
- argv += optind;
+ INIT_G();
- if (argc != 1)
- bb_show_usage();
+ sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
- target = *argv;
+ // Drop suid root privileges
+ // Need to remove SUID_NEVER from applets.h for this to work
+ //xsetuid(getuid());
+ err_str = xasprintf("interface %s %%s", device);
+ {
+ unsigned opt;
+ char *str_timeout;
- if (s < 0) {
- bb_error_msg("socket");
- exit(socket_errno);
+ /* Dad also sets quit_on_reply.
+ * Advert also sets unsolicited.
+ */
+ opt_complementary = "=1:Df:AU:c+";
+ opt = getopt32(argv, "DUAqfbc:w:I:s:",
+ &count, &str_timeout, &device, &source);
+ if (opt & 0x80) /* -w: timeout */
+ timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
+ //if (opt & 0x200) /* -s: source */
+ option_mask32 &= 0x3f; /* set respective flags */
}
+ target = argv[optind];
+
+ xfunc_error_retval = 2;
+
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
- if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
- bb_error_msg("Interface %s not found", device);
- exit(2);
- }
- ifindex = ifr.ifr_ifindex;
+ strncpy_IFNAMSIZ(ifr.ifr_name, device);
+ /* We use ifr.ifr_name in error msg so that problem
+ * with truncated name will be visible */
+ ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &ifr, err_str, "not found");
+ me.sll_ifindex = ifr.ifr_ifindex;
+
+ xioctl(sock_fd, SIOCGIFFLAGS, (char *) &ifr);
- if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
- bb_error_msg("SIOCGIFFLAGS");
- exit(2);
- }
if (!(ifr.ifr_flags & IFF_UP)) {
- bb_error_msg("Interface %s is down", device);
- exit(2);
+ bb_error_msg_and_die(err_str, "is down");
}
if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
- bb_error_msg("Interface %s is not ARPable", device);
- exit(dad ? 0 : 2);
+ bb_error_msg(err_str, "is not ARPable");
+ return (option_mask32 & DAD ? 0 : 2);
}
}
- if (!inet_aton(target, &dst)) {
- struct hostent *hp;
-
- hp = gethostbyname2(target, AF_INET);
- if (!hp) {
- bb_error_msg("invalid or unknown target %s", target);
- exit(2);
- }
- memcpy(&dst, hp->h_addr, 4);
+ /* if (!inet_aton(target, &dst)) - not needed */ {
+ len_and_sockaddr *lsa;
+ lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
+ dst = lsa->u.sin.sin_addr;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
}
if (source && !inet_aton(source, &src)) {
- bb_error_msg("invalid source address %s", source);
- exit(2);
+ bb_error_msg_and_die("invalid source address %s", source);
}
- if (!dad && unsolicited && src.s_addr == 0)
+ if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0)
src = dst;
- if (!dad || src.s_addr) {
+ if (!(option_mask32 & DAD) || src.s_addr) {
struct sockaddr_in saddr;
- int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
- if (probe_fd < 0) {
- bb_error_msg("socket");
- exit(2);
- }
- if (device) {
- if (setsockopt
- (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device,
- strlen(device) + 1) == -1)
- bb_error_msg("WARNING: interface %s is ignored", device);
- }
+ setsockopt_bindtodevice(probe_fd, device);
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
if (src.s_addr) {
+ /* Check that this is indeed our IP */
saddr.sin_addr = src;
- if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
- bb_error_msg("bind");
- exit(2);
- }
- } else if (!dad) {
- int on = 1;
- int alen = sizeof(saddr);
+ xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+ } else { /* !(option_mask32 & DAD) case */
+ /* Find IP address on this iface */
+ socklen_t alen = sizeof(saddr);
saddr.sin_port = htons(1025);
saddr.sin_addr = dst;
- if (setsockopt
- (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on,
- sizeof(on)) == -1)
- perror("WARNING: setsockopt(SO_DONTROUTE)");
- if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr))
- == -1) {
- bb_error_msg("connect");
- exit(2);
- }
- if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) ==
- -1) {
- bb_error_msg("getsockname");
- exit(2);
+ if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
+ bb_perror_msg("setsockopt(SO_DONTROUTE)");
+ xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+ if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) {
+ bb_perror_msg_and_die("getsockname");
}
+ if (saddr.sin_family != AF_INET)
+ bb_error_msg_and_die("no IP address configured");
src = saddr.sin_addr;
}
close(probe_fd);
- };
+ }
me.sll_family = AF_PACKET;
- me.sll_ifindex = ifindex;
+ //me.sll_ifindex = ifindex; - done before
me.sll_protocol = htons(ETH_P_ARP);
- if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) {
- bb_error_msg("bind");
- exit(2);
- }
+ xbind(sock_fd, (struct sockaddr *) &me, sizeof(me));
{
- int alen = sizeof(me);
+ socklen_t alen = sizeof(me);
- if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
- bb_error_msg("getsockname");
- exit(2);
+ if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1) {
+ bb_perror_msg_and_die("getsockname");
}
}
if (me.sll_halen == 0) {
- bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device);
- exit(dad ? 0 : 2);
+ bb_error_msg(err_str, "is not ARPable (no ll address)");
+ return (option_mask32 & DAD ? 0 : 2);
}
he = me;
memset(he.sll_addr, -1, he.sll_halen);
- if (!quiet) {
+ if (!(option_mask32 & QUIET)) {
+ /* inet_ntoa uses static storage, can't use in same printf */
printf("ARPING to %s", inet_ntoa(dst));
- printf(" from %s via %s\n", inet_ntoa(src),
- device ? device : "unknown");
+ printf(" from %s via %s\n", inet_ntoa(src), device);
}
- if (!src.s_addr && !dad) {
- bb_error_msg("no src address in the non-DAD mode");
- exit(2);
- }
-
- {
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_RESTART;
-
- sa.sa_handler = (void (*)(int)) finish;
- sigaction(SIGINT, &sa, NULL);
-
- sa.sa_handler = (void (*)(int)) catcher;
- sigaction(SIGALRM, &sa, NULL);
- }
+ signal_SA_RESTART_empty_mask(SIGINT, (void (*)(int))finish);
+ signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher);
catcher();
+ packet = xmalloc(4096);
while (1) {
sigset_t sset, osset;
- char packet[4096];
struct sockaddr_ll from;
- int alen = sizeof(from);
+ socklen_t alen = sizeof(from);
int cc;
- if ((cc = recvfrom(s, packet, sizeof(packet), 0,
- (struct sockaddr *) &from, &alen)) < 0) {
- perror("recvfrom");
+ cc = recvfrom(sock_fd, packet, 4096, 0, (struct sockaddr *) &from, &alen);
+ if (cc < 0) {
+ bb_perror_msg("recvfrom");
continue;
}
sigemptyset(&sset);
diff --git a/release/src/router/busybox/networking/brctl.c b/release/src/router/busybox/networking/brctl.c
new file mode 100644
index 00000000..1b526894
--- /dev/null
+++ b/release/src/router/busybox/networking/brctl.c
@@ -0,0 +1,286 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Small implementation of brctl for busybox.
+ *
+ * Copyright (C) 2008 by Bernhard Reutner-Fischer
+ *
+ * Some helper functions from bridge-utils are
+ * Copyright (C) 2000 Lennert Buytenhek
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+/* This applet currently uses only the ioctl interface and no sysfs at all.
+ * At the time of this writing this was considered a feature.
+ */
+#include "libbb.h"
+#include <linux/sockios.h>
+#include <net/if.h>
+
+#ifndef SIOCBRADDBR
+# define SIOCBRADDBR BRCTL_ADD_BRIDGE
+#endif
+#ifndef SIOCBRDELBR
+# define SIOCBRDELBR BRCTL_DEL_BRIDGE
+#endif
+#ifndef SIOCBRADDIF
+# define SIOCBRADDIF BRCTL_ADD_IF
+#endif
+#ifndef SIOCBRDELIF
+# define SIOCBRDELIF BRCTL_DEL_IF
+#endif
+
+
+/* Maximum number of ports supported per bridge interface. */
+#ifndef MAX_PORTS
+#define MAX_PORTS 32
+#endif
+
+/* Use internal number parsing and not the "exact" conversion. */
+/* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */
+#define BRCTL_USE_INTERNAL 1
+
+#if ENABLE_FEATURE_BRCTL_FANCY
+#include <linux/if_bridge.h>
+
+/* FIXME: These 4 funcs are not really clean and could be improved */
+static ALWAYS_INLINE void strtotimeval(struct timeval *tv,
+ const char *time_str)
+{
+ double secs;
+#if BRCTL_USE_INTERNAL
+ secs = /*bb_*/strtod(time_str, NULL);
+ if (!secs)
+#else
+ if (sscanf(time_str, "%lf", &secs) != 1)
+#endif
+ bb_error_msg_and_die (bb_msg_invalid_arg, time_str, "timespec");
+ tv->tv_sec = secs;
+ tv->tv_usec = 1000000 * (secs - tv->tv_sec);
+}
+
+static ALWAYS_INLINE unsigned long __tv_to_jiffies(const struct timeval *tv)
+{
+ unsigned long long jif;
+
+ jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
+
+ return jif/10000;
+}
+# if 0
+static void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
+{
+ unsigned long long tvusec;
+
+ tvusec = 10000ULL*jiffies;
+ tv->tv_sec = tvusec/1000000;
+ tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
+}
+# endif
+static unsigned long str_to_jiffies(const char *time_str)
+{
+ struct timeval tv;
+ strtotimeval(&tv, time_str);
+ return __tv_to_jiffies(&tv);
+}
+
+static void arm_ioctl(unsigned long *args,
+ unsigned long arg0, unsigned long arg1, unsigned long arg2)
+{
+ args[0] = arg0;
+ args[1] = arg1;
+ args[2] = arg2;
+ args[3] = 0;
+}
+#endif
+
+
+int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int brctl_main(int argc UNUSED_PARAM, char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ "addbr\0" "delbr\0" "addif\0" "delif\0"
+ USE_FEATURE_BRCTL_FANCY(
+ "stp\0"
+ "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
+ "setpathcost\0" "setportprio\0" "setbridgeprio\0"
+ )
+ USE_FEATURE_BRCTL_SHOW("showmacs\0" "show\0");
+
+ enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
+ USE_FEATURE_BRCTL_FANCY(,
+ ARG_stp,
+ ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
+ ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
+ )
+ USE_FEATURE_BRCTL_SHOW(, ARG_showmacs, ARG_show)
+ };
+
+ int fd;
+ smallint key;
+ struct ifreq ifr;
+ char *br, *brif;
+
+ argv++;
+ while (*argv) {
+#if ENABLE_FEATURE_BRCTL_FANCY
+ int ifidx[MAX_PORTS];
+ unsigned long args[4];
+ ifr.ifr_data = (char *) &args;
+#endif
+
+ key = index_in_strings(keywords, *argv);
+ if (key == -1) /* no match found in keywords array, bail out. */
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ argv++;
+ fd = xsocket(AF_INET, SOCK_STREAM, 0);
+
+#if ENABLE_FEATURE_BRCTL_SHOW
+ if (key == ARG_show) { /* show */
+ char brname[IFNAMSIZ];
+ int bridx[MAX_PORTS];
+ int i, num;
+ arm_ioctl(args, BRCTL_GET_BRIDGES,
+ (unsigned long) bridx, MAX_PORTS);
+ num = xioctl(fd, SIOCGIFBR, args);
+ printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
+ for (i = 0; i < num; i++) {
+ char ifname[IFNAMSIZ];
+ int j, tabs;
+ struct __bridge_info bi;
+ unsigned char *x;
+
+ if (!if_indextoname(bridx[i], brname))
+ bb_perror_msg_and_die("can't get bridge name for index %d", i);
+ strncpy_IFNAMSIZ(ifr.ifr_name, brname);
+
+ arm_ioctl(args, BRCTL_GET_BRIDGE_INFO,
+ (unsigned long) &bi, 0);
+ xioctl(fd, SIOCDEVPRIVATE, &ifr);
+ printf("%s\t\t", brname);
+
+ /* print bridge id */
+ x = (unsigned char *) &bi.bridge_id;
+ for (j = 0; j < 8; j++) {
+ printf("%.2x", x[j]);
+ if (j == 1)
+ bb_putchar('.');
+ }
+ printf(bi.stp_enabled ? "\tyes" : "\tno");
+
+ /* print interface list */
+ arm_ioctl(args, BRCTL_GET_PORT_LIST,
+ (unsigned long) ifidx, MAX_PORTS);
+ xioctl(fd, SIOCDEVPRIVATE, &ifr);
+ tabs = 0;
+ for (j = 0; j < MAX_PORTS; j++) {
+ if (!ifidx[j])
+ continue;
+ if (!if_indextoname(ifidx[j], ifname))
+ bb_perror_msg_and_die("can't get interface name for index %d", j);
+ if (tabs)
+ printf("\t\t\t\t\t");
+ else
+ tabs = 1;
+ printf("\t\t%s\n", ifname);
+ }
+ if (!tabs) /* bridge has no interfaces */
+ bb_putchar('\n');
+ }
+ goto done;
+ }
+#endif
+
+ if (!*argv) /* all but 'show' need at least one argument */
+ bb_show_usage();
+
+ br = *argv++;
+
+ if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
+ ioctl_or_perror_and_die(fd,
+ key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
+ br, "bridge %s", br);
+ goto done;
+ }
+
+ if (!*argv) /* all but 'addif/delif' need at least two arguments */
+ bb_show_usage();
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, br);
+ if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
+ brif = *argv;
+ ifr.ifr_ifindex = if_nametoindex(brif);
+ if (!ifr.ifr_ifindex) {
+ bb_perror_msg_and_die("iface %s", brif);
+ }
+ ioctl_or_perror_and_die(fd,
+ key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
+ &ifr, "bridge %s", br);
+ goto done_next_argv;
+ }
+#if ENABLE_FEATURE_BRCTL_FANCY
+ if (key == ARG_stp) { /* stp */
+ /* FIXME: parsing yes/y/on/1 versus no/n/off/0 is too involved */
+ arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE,
+ (unsigned)(**argv - '0'), 0);
+ goto fire;
+ }
+ if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
+ static const uint8_t ops[] ALIGN1 = {
+ BRCTL_SET_AGEING_TIME, /* ARG_setageing */
+ BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd */
+ BRCTL_SET_BRIDGE_HELLO_TIME, /* ARG_sethello */
+ BRCTL_SET_BRIDGE_MAX_AGE /* ARG_setmaxage */
+ };
+ arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
+ goto fire;
+ }
+ if (key == ARG_setpathcost
+ || key == ARG_setportprio
+ || key == ARG_setbridgeprio
+ ) {
+ static const uint8_t ops[] ALIGN1 = {
+ BRCTL_SET_PATH_COST, /* ARG_setpathcost */
+ BRCTL_SET_PORT_PRIORITY, /* ARG_setportprio */
+ BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */
+ };
+ int port = -1;
+ unsigned arg1, arg2;
+
+ if (key != ARG_setbridgeprio) {
+ /* get portnum */
+ unsigned i;
+
+ port = if_nametoindex(*argv++);
+ if (!port)
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, "port");
+ memset(ifidx, 0, sizeof ifidx);
+ arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
+ MAX_PORTS);
+ xioctl(fd, SIOCDEVPRIVATE, &ifr);
+ for (i = 0; i < MAX_PORTS; i++) {
+ if (ifidx[i] == port) {
+ port = i;
+ break;
+ }
+ }
+ }
+ arg1 = port;
+ arg2 = xatoi_u(*argv);
+ if (key == ARG_setbridgeprio) {
+ arg1 = arg2;
+ arg2 = 0;
+ }
+ arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2);
+ }
+ fire:
+ /* Execute the previously set command */
+ xioctl(fd, SIOCDEVPRIVATE, &ifr);
+#endif
+ done_next_argv:
+ argv++;
+ done:
+ close(fd);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/networking/dnsd.c b/release/src/router/busybox/networking/dnsd.c
new file mode 100644
index 00000000..56ede3fc
--- /dev/null
+++ b/release/src/router/busybox/networking/dnsd.c
@@ -0,0 +1,518 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini DNS server implementation for busybox
+ *
+ * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name)
+ * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no)
+ * Copyright (C) 2003 Paul Sheer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote
+ * it into a shape which I believe is both easier to understand and maintain.
+ * I also reused the input buffer for output and removed services he did not
+ * need. [1] http://threading.2038bug.com/sheerdns/
+ *
+ * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
+ * the first porting of oao' scdns to busybox also.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+
+//#define DEBUG 1
+#define DEBUG 0
+
+enum {
+ /* can tweak this */
+ DEFAULT_TTL = 120,
+
+ /* cannot get bigger packets than 512 per RFC1035. */
+ MAX_PACK_LEN = 512,
+ IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"),
+ MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"),
+ REQ_A = 1,
+ REQ_PTR = 12,
+};
+
+/* the message from client and first part of response msg */
+struct dns_head {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t nquer;
+ uint16_t nansw;
+ uint16_t nauth;
+ uint16_t nadd;
+};
+struct dns_prop {
+ uint16_t type;
+ uint16_t class;
+};
+/* element of known name, ip address and reversed ip address */
+struct dns_entry {
+ struct dns_entry *next;
+ uint32_t ip;
+ char rip[IP_STRING_LEN]; /* length decimal reversed IP */
+ char name[1];
+};
+
+#define OPT_verbose (option_mask32)
+
+
+/*
+ * Insert length of substrings instead of dots
+ */
+static void undot(char *rip)
+{
+ int i = 0;
+ int s = 0;
+
+ while (rip[i])
+ i++;
+ for (--i; i >= 0; i--) {
+ if (rip[i] == '.') {
+ rip[i] = s;
+ s = 0;
+ } else {
+ s++;
+ }
+ }
+}
+
+/*
+ * Read hostname/IP records from file
+ */
+static struct dns_entry *parse_conf_file(const char *fileconf)
+{
+ char *token[2];
+ parser_t *parser;
+ struct dns_entry *m, *conf_data;
+ struct dns_entry **nextp;
+
+ conf_data = NULL;
+ nextp = &conf_data;
+
+ parser = config_open(fileconf);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+ struct in_addr ip;
+ uint32_t v32;
+
+ if (inet_aton(token[1], &ip) == 0) {
+ bb_error_msg("error at line %u, skipping", parser->lineno);
+ continue;
+ }
+
+ if (OPT_verbose)
+ bb_error_msg("name:%s, ip:%s", token[0], token[1]);
+
+ /* sizeof(*m) includes 1 byte for m->name[0] */
+ m = xzalloc(sizeof(*m) + strlen(token[0]) + 1);
+ /*m->next = NULL;*/
+ *nextp = m;
+ nextp = &m->next;
+
+ m->name[0] = '.';
+ strcpy(m->name + 1, token[0]);
+ undot(m->name);
+ m->ip = ip.s_addr; /* in network order */
+ v32 = ntohl(m->ip);
+ /* inverted order */
+ sprintf(m->rip, ".%u.%u.%u.%u",
+ (uint8_t)(v32),
+ (uint8_t)(v32 >> 8),
+ (uint8_t)(v32 >> 16),
+ (v32 >> 24)
+ );
+ undot(m->rip);
+ }
+ config_close(parser);
+ return conf_data;
+}
+
+/*
+ * Look query up in dns records and return answer if found.
+ */
+static char *table_lookup(struct dns_entry *d,
+ uint16_t type,
+ char* query_string)
+{
+ while (d) {
+ unsigned len = d->name[0];
+ /* d->name[len] is the last (non NUL) char */
+#if DEBUG
+ char *p, *q;
+ q = query_string + 1;
+ p = d->name + 1;
+ fprintf(stderr, "%d/%d p:%s q:%s %d\n",
+ (int)strlen(p), len,
+ p, q, (int)strlen(q)
+ );
+#endif
+ if (type == htons(REQ_A)) {
+ /* search by host name */
+ if (len != 1 || d->name[1] != '*') {
+/* we are lax, hope no name component is ever >64 so that length
+ * (which will be represented as 'A','B'...) matches a lowercase letter.
+ * Actually, I think false matches are hard to construct.
+ * Example.
+ * [31] len is represented as '1', [65] as 'A', [65+32] as 'a'.
+ * [65] <65 same chars>[31]<31 same chars>NUL
+ * [65+32]<65 same chars>1 <31 same chars>NUL
+ * This example seems to be the minimal case when false match occurs.
+ */
+ if (strcasecmp(d->name, query_string) != 0)
+ goto next;
+ }
+ return (char *)&d->ip;
+#if DEBUG
+ fprintf(stderr, "Found IP:%x\n", (int)d->ip);
+#endif
+ return 0;
+ }
+ /* search by IP-address */
+ if ((len != 1 || d->name[1] != '*')
+ /* we assume (do not check) that query_string
+ * ends in ".in-addr.arpa" */
+ && strncmp(d->rip, query_string, strlen(d->rip)) == 0
+ ) {
+#if DEBUG
+ fprintf(stderr, "Found name:%s\n", d->name);
+#endif
+ return d->name;
+ }
+ next:
+ d = d->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Decode message and generate answer
+ */
+/* RFC 1035
+...
+Whenever an octet represents a numeric quantity, the left most bit
+in the diagram is the high order or most significant bit.
+That is, the bit labeled 0 is the most significant bit.
+...
+
+4.1.1. Header section format
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ID |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ |QR| OPCODE |AA|TC|RD|RA| 0 0 0| RCODE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QDCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ANCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | NSCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ARCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ID 16 bit random identifier assigned by querying peer.
+ Used to match query/response.
+QR message is a query (0), or a response (1).
+OPCODE 0 standard query (QUERY)
+ 1 inverse query (IQUERY)
+ 2 server status request (STATUS)
+AA Authoritative Answer - this bit is valid in responses.
+ Responding name server is an authority for the domain name
+ in question section. Answer section may have multiple owner names
+ because of aliases. The AA bit corresponds to the name which matches
+ the query name, or the first owner name in the answer section.
+TC TrunCation - this message was truncated.
+RD Recursion Desired - this bit may be set in a query and
+ is copied into the response. If RD is set, it directs
+ the name server to pursue the query recursively.
+ Recursive query support is optional.
+RA Recursion Available - this be is set or cleared in a
+ response, and denotes whether recursive query support is
+ available in the name server.
+RCODE Response code.
+ 0 No error condition
+ 1 Format error
+ 2 Server failure - server was unable to process the query
+ due to a problem with the name server.
+ 3 Name Error - meaningful only for responses from
+ an authoritative name server. The referenced domain name
+ does not exist.
+ 4 Not Implemented.
+ 5 Refused.
+QDCOUNT number of entries in the question section.
+ANCOUNT number of records in the answer section.
+NSCOUNT number of records in the authority records section.
+ARCOUNT number of records in the additional records section.
+
+4.1.2. Question section format
+
+The section contains QDCOUNT (usually 1) entries, each of this format:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / QNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QTYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QCLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+QNAME a domain name represented as a sequence of labels, where
+ each label consists of a length octet followed by that
+ number of octets. The domain name terminates with the
+ zero length octet for the null label of the root. Note
+ that this field may be an odd number of octets; no
+ padding is used.
+QTYPE a two octet type of the query.
+ 1 a host address [REQ_A const]
+ 2 an authoritative name server
+ 3 a mail destination (Obsolete - use MX)
+ 4 a mail forwarder (Obsolete - use MX)
+ 5 the canonical name for an alias
+ 6 marks the start of a zone of authority
+ 7 a mailbox domain name (EXPERIMENTAL)
+ 8 a mail group member (EXPERIMENTAL)
+ 9 a mail rename domain name (EXPERIMENTAL)
+ 10 a null RR (EXPERIMENTAL)
+ 11 a well known service description
+ 12 a domain name pointer [REQ_PTR const]
+ 13 host information
+ 14 mailbox or mail list information
+ 15 mail exchange
+ 16 text strings
+ 0x1c IPv6?
+ 252 a request for a transfer of an entire zone
+ 253 a request for mailbox-related records (MB, MG or MR)
+ 254 a request for mail agent RRs (Obsolete - see MX)
+ 255 a request for all records
+QCLASS a two octet code that specifies the class of the query.
+ 1 the Internet
+ (others are historic only)
+ 255 any class
+
+4.1.3. Resource record format
+
+The answer, authority, and additional sections all share the same format:
+a variable number of resource records, where the number of records
+is specified in the corresponding count field in the header.
+Each resource record has this format:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / /
+ / NAME /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | CLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TTL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RDLENGTH |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ / RDATA /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+NAME a domain name to which this resource record pertains.
+TYPE two octets containing one of the RR type codes. This
+ field specifies the meaning of the data in the RDATA field.
+CLASS two octets which specify the class of the data in the RDATA field.
+TTL a 32 bit unsigned integer that specifies the time interval
+ (in seconds) that the record may be cached.
+RDLENGTH a 16 bit integer, length in octets of the RDATA field.
+RDATA a variable length string of octets that describes the resource.
+ The format of this information varies according to the TYPE
+ and CLASS of the resource record.
+ If the TYPE is A and the CLASS is IN, it's a 4 octet IP address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, domain names coan be compressed.
+An entire domain name or a list of labels at the end of a domain name
+is replaced with a pointer to a prior occurance of the same name.
+
+The pointer takes the form of a two octet sequence:
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | 1 1| OFFSET |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+The first two bits are ones. This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less. The OFFSET field specifies
+an offset from the start of the message (i.e., the first octet
+of the ID field in the domain header).
+A zero offset specifies the first byte of the ID field, etc.
+Domain name in a message can be represented as either:
+ - a sequence of labels ending in a zero octet
+ - a pointer
+ - a sequence of labels ending with a pointer
+ */
+static int process_packet(struct dns_entry *conf_data,
+ uint32_t conf_ttl,
+ uint8_t *buf)
+{
+ char *answstr;
+ struct dns_head *head;
+ struct dns_prop *unaligned_qprop;
+ char *query_string;
+ uint8_t *answb;
+ uint16_t outr_rlen;
+ uint16_t outr_flags;
+ uint16_t type;
+ uint16_t class;
+ int querystr_len;
+
+ head = (struct dns_head *)buf;
+ if (head->nquer == 0) {
+ bb_error_msg("packet has 0 queries, ignored");
+ return -1;
+ }
+
+ if (head->flags & htons(0x8000)) { /* QR bit */
+ bb_error_msg("response packet, ignored");
+ return -1;
+ }
+
+ /* start of query string */
+ query_string = (void *)(head + 1);
+ /* caller guarantees strlen is <= MAX_PACK_LEN */
+ querystr_len = strlen(query_string) + 1;
+ /* may be unaligned! */
+ unaligned_qprop = (void *)(query_string + querystr_len);
+ querystr_len += sizeof(unaligned_qprop);
+ /* where to append answer block */
+ answb = (void *)(unaligned_qprop + 1);
+
+ /* QR = 1 "response", RCODE = 4 "Not Implemented" */
+ outr_flags = htons(0x8000 | 4);
+
+ move_from_unaligned16(type, &unaligned_qprop->type);
+ if (type != htons(REQ_A) && type != htons(REQ_PTR)) {
+ /* we can't handle the query type */
+ goto empty_packet;
+ }
+ move_from_unaligned16(class, &unaligned_qprop->class);
+ if (class != htons(1)) { /* not class INET? */
+ goto empty_packet;
+ }
+ /* OPCODE != 0 "standard query" ? */
+ if ((head->flags & htons(0x7800)) != 0) {
+ goto empty_packet;
+ }
+
+ /* look up the name */
+#if DEBUG
+ /* need to convert lengths to dots before we can use it in non-debug */
+ bb_info_msg("%s", query_string);
+#endif
+ answstr = table_lookup(conf_data, type, query_string);
+ outr_rlen = 4;
+ if (answstr && type == htons(REQ_PTR)) {
+ /* return a host name */
+ outr_rlen = strlen(answstr) + 1;
+ }
+ if (!answstr
+ || (unsigned)(answb - buf) + querystr_len + 4 + 2 + outr_rlen > MAX_PACK_LEN
+ ) {
+ /* QR = 1 "response"
+ * AA = 1 "Authoritative Answer"
+ * RCODE = 3 "Name Error" */
+ outr_flags = htons(0x8000 | 0x0400 | 3);
+ goto empty_packet;
+ }
+
+ /* copy query block to answer block */
+ memcpy(answb, query_string, querystr_len);
+ answb += querystr_len;
+ /* append answer Resource Record */
+ move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl));
+ answb += 4;
+ move_to_unaligned32((uint16_t *)answb, htons(outr_rlen));
+ answb += 2;
+ memcpy(answb, answstr, outr_rlen);
+ answb += outr_rlen;
+
+ /* QR = 1 "response",
+ * AA = 1 "Authoritative Answer",
+ * RCODE = 0 "success" */
+ outr_flags = htons(0x8000 | 0x0400 | 0);
+ /* we have one answer */
+ head->nansw = htons(1);
+
+ empty_packet:
+ head->flags |= outr_flags;
+ head->nauth = head->nadd = 0;
+ head->nquer = htons(1); // why???
+
+ return answb - buf;
+}
+
+int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dnsd_main(int argc UNUSED_PARAM, char **argv)
+{
+ const char *listen_interface = "0.0.0.0";
+ const char *fileconf = "/etc/dnsd.conf";
+ struct dns_entry *conf_data;
+ uint32_t conf_ttl = DEFAULT_TTL;
+ char *sttl, *sport;
+ len_and_sockaddr *lsa, *from, *to;
+ unsigned lsa_size;
+ int udps, opts;
+ uint16_t port = 53;
+ uint8_t buf[MAX_PACK_LEN + 1];
+
+ opts = getopt32(argv, "vi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport);
+ //if (opts & 0x1) // -v
+ //if (opts & 0x2) // -i
+ //if (opts & 0x4) // -c
+ if (opts & 0x8) // -t
+ conf_ttl = xatou_range(sttl, 1, 0xffffffff);
+ if (opts & 0x10) // -p
+ port = xatou_range(sport, 1, 0xffff);
+ if (opts & 0x20) { // -d
+ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+ /* Clear all except "verbose" bit */
+ option_mask32 &= 1;
+
+ conf_data = parse_conf_file(fileconf);
+
+ lsa = xdotted2sockaddr(listen_interface, port);
+ udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+ xbind(udps, &lsa->u.sa, lsa->len);
+ socket_want_pktinfo(udps); /* needed for recv_from_to to work */
+ lsa_size = LSA_LEN_SIZE + lsa->len;
+ from = xzalloc(lsa_size);
+ to = xzalloc(lsa_size);
+
+ {
+ char *p = xmalloc_sockaddr2dotted(&lsa->u.sa);
+ bb_info_msg("Accepting UDP packets on %s", p);
+ free(p);
+ }
+
+ while (1) {
+ int r;
+ /* Try to get *DEST* address (to which of our addresses
+ * this query was directed), and reply from the same address.
+ * Or else we can exhibit usual UDP ugliness:
+ * [ip1.multihomed.ip2] <= query to ip1 <= peer
+ * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
+ memcpy(to, lsa, lsa_size);
+ r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
+ if (r < 12 || r > MAX_PACK_LEN) {
+ bb_error_msg("packet size %d, ignored", r);
+ continue;
+ }
+ if (OPT_verbose)
+ bb_info_msg("Got UDP packet");
+ buf[r] = '\0'; /* paranoia */
+ r = process_packet(conf_data, conf_ttl, buf);
+ if (r <= 0)
+ continue;
+ send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);
+ }
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/ether-wake.c b/release/src/router/busybox/networking/ether-wake.c
new file mode 100644
index 00000000..882429d1
--- /dev/null
+++ b/release/src/router/busybox/networking/ether-wake.c
@@ -0,0 +1,276 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ether-wake.c - Send a magic packet to wake up sleeping machines.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html
+ * Busybox port: Christian Volkmann <haveaniceday@online.de>
+ * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
+ */
+
+/* full usage according Donald Becker
+ * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
+ *
+ * This program generates and transmits a Wake-On-LAN (WOL)\n"
+ * \"Magic Packet\", used for restarting machines that have been\n"
+ * soft-powered-down (ACPI D3-warm state).\n"
+ * It currently generates the standard AMD Magic Packet format, with\n"
+ * an optional password appended.\n"
+ *
+ * The single required parameter is the Ethernet MAC (station) address\n"
+ * of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
+ * The MAC address may be found with the 'arp' program while the target\n"
+ * machine is awake.\n"
+ *
+ * Options:\n"
+ * -b Send wake-up packet to the broadcast address.\n"
+ * -D Increase the debug level.\n"
+ * -i ifname Use interface IFNAME instead of the default 'eth0'.\n"
+ * -p <pw> Append the four or six byte password PW to the packet.\n"
+ * A password is only required for a few adapter types.\n"
+ * The password may be specified in ethernet hex format\n"
+ * or dotted decimal (Internet address)\n"
+ * -p 00:22:44:66:88:aa\n"
+ * -p 192.168.1.1\n";
+ *
+ *
+ * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
+ * used for restarting machines that have been soft-powered-down
+ * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet
+ * format, with an optional password appended.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ * Contact the author for use under other terms.
+ *
+ * This source file was originally part of the network tricks package, and
+ * is now distributed to support the Scyld Beowulf system.
+ * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
+ *
+ * The author may be reached as becker@scyld, or C/O
+ * Scyld Computing Corporation
+ * 914 Bay Ridge Road, Suite 220
+ * Annapolis MD 21403
+ *
+ * Notes:
+ * On some systems dropping root capability allows the process to be
+ * dumped, traced or debugged.
+ * If someone traces this program, they get control of a raw socket.
+ * Linux handles this safely, but beware when porting this program.
+ *
+ * An alternative to needing 'root' is using a UDP broadcast socket, however
+ * doing so only works with adapters configured for unicast+broadcast Rx
+ * filter. That configuration consumes more power.
+*/
+
+
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <linux/if.h>
+
+#include "libbb.h"
+
+/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
+ * work as non-root, but we need SOCK_PACKET to specify the Ethernet
+ * destination address.
+ */
+#ifdef PF_PACKET
+# define whereto_t sockaddr_ll
+# define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0)
+#else
+# define whereto_t sockaddr
+# define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET)
+#endif
+
+#ifdef DEBUG
+# define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args)
+void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
+{
+ int i;
+ printf("packet dump:\n");
+ for (i = 0; i < pktsize; ++i) {
+ printf("%2.2x ", outpack[i]);
+ if (i % 20 == 19) bb_putchar('\n');
+ }
+ printf("\n\n");
+}
+#else
+# define bb_debug_msg(fmt, args...) ((void)0)
+# define bb_debug_dump_packet(outpack, pktsize) ((void)0)
+#endif
+
+/* Convert the host ID string to a MAC address.
+ * The string may be a:
+ * Host name
+ * IP address string
+ * MAC address string
+*/
+static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
+{
+ struct ether_addr *eap;
+
+ eap = ether_aton(hostid);
+ if (eap) {
+ *eaddr = *eap;
+ bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr));
+#if !defined(__UCLIBC__)
+ } else if (ether_hostton(hostid, eaddr) == 0) {
+ bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
+#endif
+ } else
+ bb_show_usage();
+}
+
+static int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
+{
+ int i;
+ unsigned char *station_addr = eaddr->ether_addr_octet;
+
+ memset(pkt, 0xff, 6);
+ if (!broadcast)
+ memcpy(pkt, station_addr, 6);
+ pkt += 6;
+
+ memcpy(pkt, station_addr, 6); /* 6 */
+ pkt += 6;
+
+ *pkt++ = 0x08; /* 12 */ /* Or 0x0806 for ARP, 0x8035 for RARP */
+ *pkt++ = 0x42; /* 13 */
+
+ memset(pkt, 0xff, 6); /* 14 */
+
+ for (i = 0; i < 16; ++i) {
+ pkt += 6;
+ memcpy(pkt, station_addr, 6); /* 20,26,32,... */
+ }
+
+ return 20 + 16*6; /* length of packet */
+}
+
+static int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
+{
+ unsigned passwd[6];
+ int byte_cnt, i;
+
+ /* handle MAC format */
+ byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &passwd[0], &passwd[1], &passwd[2],
+ &passwd[3], &passwd[4], &passwd[5]);
+ /* handle IP format */
+// FIXME: why < 4?? should it be < 6?
+ if (byte_cnt < 4)
+ byte_cnt = sscanf(ethoptarg, "%u.%u.%u.%u",
+ &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
+ if (byte_cnt < 4) {
+ bb_error_msg("cannot read Wake-On-LAN pass");
+ return 0;
+ }
+// TODO: check invalid numbers >255??
+ for (i = 0; i < byte_cnt; ++i)
+ wol_passwd[i] = passwd[i];
+
+ bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n",
+ wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3],
+ byte_cnt);
+
+ return byte_cnt;
+}
+
+int ether_wake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ether_wake_main(int argc UNUSED_PARAM, char **argv)
+{
+ const char *ifname = "eth0";
+ char *pass;
+ unsigned flags;
+ unsigned char wol_passwd[6];
+ int wol_passwd_sz = 0;
+ int s; /* Raw socket */
+ int pktsize;
+ unsigned char outpack[1000];
+
+ struct ether_addr eaddr;
+ struct whereto_t whereto; /* who to wake up */
+
+ /* handle misc user options */
+ opt_complementary = "=1";
+ flags = getopt32(argv, "bi:p:", &ifname, &pass);
+ if (flags & 4) /* -p */
+ wol_passwd_sz = get_wol_pw(pass, wol_passwd);
+ flags &= 1; /* we further interested only in -b [bcast] flag */
+
+ /* create the raw socket */
+ s = make_socket();
+
+ /* now that we have a raw socket we can drop root */
+ /* xsetuid(getuid()); - but save on code size... */
+
+ /* look up the dest mac address */
+ get_dest_addr(argv[optind], &eaddr);
+
+ /* fill out the header of the packet */
+ pktsize = get_fill(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */);
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* Fill in the source address, if possible. */
+#ifdef __linux__
+ {
+ struct ifreq if_hwaddr;
+
+ strncpy_IFNAMSIZ(if_hwaddr.ifr_name, ifname);
+ ioctl_or_perror_and_die(s, SIOCGIFHWADDR, &if_hwaddr, "SIOCGIFHWADDR on %s failed", ifname);
+
+ memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
+
+# ifdef DEBUG
+ {
+ unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
+ printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
+ if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
+ hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
+ }
+# endif
+ }
+#endif /* __linux__ */
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* append the password if specified */
+ if (wol_passwd_sz > 0) {
+ memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
+ pktsize += wol_passwd_sz;
+ }
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* This is necessary for broadcasts to work */
+ if (flags /* & 1 OPT_BROADCAST */) {
+ if (setsockopt_broadcast(s) != 0)
+ bb_perror_msg("SO_BROADCAST");
+ }
+
+#if defined(PF_PACKET)
+ {
+ struct ifreq ifr;
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ xioctl(s, SIOCGIFINDEX, &ifr);
+ memset(&whereto, 0, sizeof(whereto));
+ whereto.sll_family = AF_PACKET;
+ whereto.sll_ifindex = ifr.ifr_ifindex;
+ /* The manual page incorrectly claims the address must be filled.
+ We do so because the code may change to match the docs. */
+ whereto.sll_halen = ETH_ALEN;
+ memcpy(whereto.sll_addr, outpack, ETH_ALEN);
+ }
+#else
+ whereto.sa_family = 0;
+ strcpy(whereto.sa_data, ifname);
+#endif
+ xsendto(s, outpack, pktsize, (struct sockaddr *)&whereto, sizeof(whereto));
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(s);
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/networking/ftpd.c b/release/src/router/busybox/networking/ftpd.c
new file mode 100644
index 00000000..e85e4f8e
--- /dev/null
+++ b/release/src/router/busybox/networking/ftpd.c
@@ -0,0 +1,1351 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Simple FTP daemon, based on vsftpd 2.0.7 (written by Chris Evans)
+ *
+ * Author: Adam Tkac <vonsch@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ *
+ * Only subset of FTP protocol is implemented but vast majority of clients
+ * should not have any problem.
+ *
+ * You have to run this daemon via inetd.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <netinet/tcp.h>
+
+#define FTP_DATACONN 150
+#define FTP_NOOPOK 200
+#define FTP_TYPEOK 200
+#define FTP_PORTOK 200
+#define FTP_STRUOK 200
+#define FTP_MODEOK 200
+#define FTP_ALLOOK 202
+#define FTP_STATOK 211
+#define FTP_STATFILE_OK 213
+#define FTP_HELP 214
+#define FTP_SYSTOK 215
+#define FTP_GREET 220
+#define FTP_GOODBYE 221
+#define FTP_TRANSFEROK 226
+#define FTP_PASVOK 227
+/*#define FTP_EPRTOK 228*/
+#define FTP_EPSVOK 229
+#define FTP_LOGINOK 230
+#define FTP_CWDOK 250
+#define FTP_RMDIROK 250
+#define FTP_DELEOK 250
+#define FTP_RENAMEOK 250
+#define FTP_PWDOK 257
+#define FTP_MKDIROK 257
+#define FTP_GIVEPWORD 331
+#define FTP_RESTOK 350
+#define FTP_RNFROK 350
+#define FTP_TIMEOUT 421
+#define FTP_BADSENDCONN 425
+#define FTP_BADSENDNET 426
+#define FTP_BADSENDFILE 451
+#define FTP_BADCMD 500
+#define FTP_COMMANDNOTIMPL 502
+#define FTP_NEEDUSER 503
+#define FTP_NEEDRNFR 503
+#define FTP_BADSTRU 504
+#define FTP_BADMODE 504
+#define FTP_LOGINERR 530
+#define FTP_FILEFAIL 550
+#define FTP_NOPERM 550
+#define FTP_UPLOADFAIL 553
+
+#define STR1(s) #s
+#define STR(s) STR1(s)
+
+/* Convert a constant to 3-digit string, packed into uint32_t */
+enum {
+ /* Shift for Nth decimal digit */
+ SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
+ SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
+ SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
+ /* And for 4th position (space) */
+ SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
+};
+#define STRNUM32(s) (uint32_t)(0 \
+ | (('0' + ((s) / 1 % 10)) << SHIFT0) \
+ | (('0' + ((s) / 10 % 10)) << SHIFT1) \
+ | (('0' + ((s) / 100 % 10)) << SHIFT2) \
+)
+#define STRNUM32sp(s) (uint32_t)(0 \
+ | (' ' << SHIFTsp) \
+ | (('0' + ((s) / 1 % 10)) << SHIFT0) \
+ | (('0' + ((s) / 10 % 10)) << SHIFT1) \
+ | (('0' + ((s) / 100 % 10)) << SHIFT2) \
+)
+
+#define MSG_OK "Operation successful\r\n"
+#define MSG_ERR "Error\r\n"
+
+struct globals {
+ int pasv_listen_fd;
+#if !BB_MMU
+ int root_fd;
+#endif
+ int local_file_fd;
+ unsigned end_time;
+ unsigned timeout;
+ unsigned verbose;
+ off_t local_file_pos;
+ off_t restart_pos;
+ len_and_sockaddr *local_addr;
+ len_and_sockaddr *port_addr;
+ char *ftp_cmd;
+ char *ftp_arg;
+#if ENABLE_FEATURE_FTP_WRITE
+ char *rnfr_filename;
+#endif
+ /* We need these aligned to uint32_t */
+ char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
+ char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+ /* Moved to main */ \
+ /*strcpy(G.msg_ok + 4, MSG_OK );*/ \
+ /*strcpy(G.msg_err + 4, MSG_ERR);*/ \
+} while (0)
+
+
+static char *
+escape_text(const char *prepend, const char *str, unsigned escapee)
+{
+ unsigned retlen, remainlen, chunklen;
+ char *ret, *found;
+ char append;
+
+ append = (char)escapee;
+ escapee >>= 8;
+
+ remainlen = strlen(str);
+ retlen = strlen(prepend);
+ ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
+ strcpy(ret, prepend);
+
+ for (;;) {
+ found = strchrnul(str, escapee);
+ chunklen = found - str + 1;
+
+ /* Copy chunk up to and including escapee (or NUL) to ret */
+ memcpy(ret + retlen, str, chunklen);
+ retlen += chunklen;
+
+ if (*found == '\0') {
+ /* It wasn't escapee, it was NUL! */
+ ret[retlen - 1] = append; /* replace NUL */
+ ret[retlen] = '\0'; /* add NUL */
+ break;
+ }
+ ret[retlen++] = escapee; /* duplicate escapee */
+ str = found + 1;
+ }
+ return ret;
+}
+
+/* Returns strlen as a bonus */
+static unsigned
+replace_char(char *str, char from, char to)
+{
+ char *p = str;
+ while (*p) {
+ if (*p == from)
+ *p = to;
+ p++;
+ }
+ return p - str;
+}
+
+static void
+verbose_log(const char *str)
+{
+ bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
+}
+
+/* NB: status_str is char[4] packed into uint32_t */
+static void
+cmdio_write(uint32_t status_str, const char *str)
+{
+ char *response;
+ int len;
+
+ /* FTP uses telnet protocol for command link.
+ * In telnet, 0xff is an escape char, and needs to be escaped: */
+ response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
+
+ /* FTP sends embedded LFs as NULs */
+ len = replace_char(response, '\n', '\0');
+
+ response[len++] = '\n'; /* tack on trailing '\n' */
+ xwrite(STDOUT_FILENO, response, len);
+ if (G.verbose > 1)
+ verbose_log(response);
+ free(response);
+}
+
+static void
+cmdio_write_ok(unsigned status)
+{
+ *(uint32_t *) G.msg_ok = status;
+ xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
+ if (G.verbose > 1)
+ verbose_log(G.msg_ok);
+}
+#define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
+
+/* TODO: output strerr(errno) if errno != 0? */
+static void
+cmdio_write_error(unsigned status)
+{
+ *(uint32_t *) G.msg_err = status;
+ xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
+ if (G.verbose > 1)
+ verbose_log(G.msg_err);
+}
+#define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
+
+static void
+cmdio_write_raw(const char *p_text)
+{
+ xwrite_str(STDOUT_FILENO, p_text);
+ if (G.verbose > 1)
+ verbose_log(p_text);
+}
+
+static void
+timeout_handler(int sig UNUSED_PARAM)
+{
+ off_t pos;
+ int sv_errno = errno;
+
+ if ((int)(monotonic_sec() - G.end_time) >= 0)
+ goto timed_out;
+
+ if (!G.local_file_fd)
+ goto timed_out;
+
+ pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
+ if (pos == G.local_file_pos)
+ goto timed_out;
+ G.local_file_pos = pos;
+
+ alarm(G.timeout);
+ errno = sv_errno;
+ return;
+
+ timed_out:
+ cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
+/* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
+ exit(1);
+}
+
+/* Simple commands */
+
+static void
+handle_pwd(void)
+{
+ char *cwd, *response;
+
+ cwd = xrealloc_getcwd_or_warn(NULL);
+ if (cwd == NULL)
+ cwd = xstrdup("");
+
+ /* We have to promote each " to "" */
+ response = escape_text(" \"", cwd, ('"' << 8) + '"');
+ free(cwd);
+ cmdio_write(STRNUM32(FTP_PWDOK), response);
+ free(response);
+}
+
+static void
+handle_cwd(void)
+{
+ if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+ WRITE_OK(FTP_CWDOK);
+}
+
+static void
+handle_cdup(void)
+{
+ G.ftp_arg = (char*)"..";
+ handle_cwd();
+}
+
+static void
+handle_stat(void)
+{
+ cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
+ " TYPE: BINARY\r\n"
+ STR(FTP_STATOK)" Ok\r\n");
+}
+
+/* Examples of HELP and FEAT:
+# nc -vvv ftp.kernel.org 21
+ftp.kernel.org (130.239.17.4:21) open
+220 Welcome to ftp.kernel.org.
+FEAT
+211-Features:
+ EPRT
+ EPSV
+ MDTM
+ PASV
+ REST STREAM
+ SIZE
+ TVFS
+ UTF8
+211 End
+HELP
+214-The following commands are recognized.
+ ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
+ MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
+ RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
+ XPWD XRMD
+214 Help OK.
+*/
+static void
+handle_feat(unsigned status)
+{
+ cmdio_write(status, "-Features:");
+ cmdio_write_raw(" EPSV\r\n"
+ " PASV\r\n"
+ " REST STREAM\r\n"
+ " MDTM\r\n"
+ " SIZE\r\n");
+ cmdio_write(status, " Ok");
+}
+
+/* Download commands */
+
+static inline int
+port_active(void)
+{
+ return (G.port_addr != NULL);
+}
+
+static inline int
+pasv_active(void)
+{
+ return (G.pasv_listen_fd > STDOUT_FILENO);
+}
+
+static void
+port_pasv_cleanup(void)
+{
+ free(G.port_addr);
+ G.port_addr = NULL;
+ if (G.pasv_listen_fd > STDOUT_FILENO)
+ close(G.pasv_listen_fd);
+ G.pasv_listen_fd = -1;
+}
+
+/* On error, emits error code to the peer */
+static int
+ftpdataio_get_pasv_fd(void)
+{
+ int remote_fd;
+
+ remote_fd = accept(G.pasv_listen_fd, NULL, 0);
+
+ if (remote_fd < 0) {
+ WRITE_ERR(FTP_BADSENDCONN);
+ return remote_fd;
+ }
+
+ setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ return remote_fd;
+}
+
+/* Clears port/pasv data.
+ * This means we dont waste resources, for example, keeping
+ * PASV listening socket open when it is no longer needed.
+ * On error, emits error code to the peer (or exits).
+ * On success, emits p_status_msg to the peer.
+ */
+static int
+get_remote_transfer_fd(const char *p_status_msg)
+{
+ int remote_fd;
+
+ if (pasv_active())
+ /* On error, emits error code to the peer */
+ remote_fd = ftpdataio_get_pasv_fd();
+ else
+ /* Exits on error */
+ remote_fd = xconnect_stream(G.port_addr);
+
+ port_pasv_cleanup();
+
+ if (remote_fd < 0)
+ return remote_fd;
+
+ cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
+ return remote_fd;
+}
+
+/* If there were neither PASV nor PORT, emits error code to the peer */
+static int
+port_or_pasv_was_seen(void)
+{
+ if (!pasv_active() && !port_active()) {
+ cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Exits on error */
+static unsigned
+bind_for_passive_mode(void)
+{
+ int fd;
+ unsigned port;
+
+ port_pasv_cleanup();
+
+ G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
+ setsockopt_reuseaddr(fd);
+
+ set_nport(G.local_addr, 0);
+ xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
+ xlisten(fd, 1);
+ getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
+
+ port = get_nport(&G.local_addr->u.sa);
+ port = ntohs(port);
+ return port;
+}
+
+/* Exits on error */
+static void
+handle_pasv(void)
+{
+ unsigned port;
+ char *addr, *response;
+
+ port = bind_for_passive_mode();
+
+ if (G.local_addr->u.sa.sa_family == AF_INET)
+ addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
+ else /* seen this in the wild done by other ftp servers: */
+ addr = xstrdup("0.0.0.0");
+ replace_char(addr, '.', ',');
+
+ response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
+ addr, (int)(port >> 8), (int)(port & 255));
+ free(addr);
+ cmdio_write_raw(response);
+ free(response);
+}
+
+/* Exits on error */
+static void
+handle_epsv(void)
+{
+ unsigned port;
+ char *response;
+
+ port = bind_for_passive_mode();
+ response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
+ cmdio_write_raw(response);
+ free(response);
+}
+
+/* libbb candidate */
+static
+len_and_sockaddr* get_peer_lsa(int fd)
+{
+ len_and_sockaddr *lsa;
+ socklen_t len = 0;
+
+ if (getpeername(fd, NULL, &len) != 0)
+ return NULL;
+ lsa = xzalloc(LSA_LEN_SIZE + len);
+ lsa->len = len;
+ getpeername(fd, &lsa->u.sa, &lsa->len);
+ return lsa;
+}
+
+static void
+handle_port(void)
+{
+ unsigned port, port_hi;
+ char *raw, *comma;
+#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
+ socklen_t peer_ipv4_len;
+ struct sockaddr_in peer_ipv4;
+ struct in_addr port_ipv4_sin_addr;
+#endif
+
+ port_pasv_cleanup();
+
+ raw = G.ftp_arg;
+
+ /* PORT command format makes sense only over IPv4 */
+ if (!raw
+#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
+ || G.local_addr->u.sa.sa_family != AF_INET
+#endif
+ ) {
+ bail:
+ WRITE_ERR(FTP_BADCMD);
+ return;
+ }
+
+ comma = strrchr(raw, ',');
+ if (comma == NULL)
+ goto bail;
+ *comma = '\0';
+ port = bb_strtou(&comma[1], NULL, 10);
+ if (errno || port > 0xff)
+ goto bail;
+
+ comma = strrchr(raw, ',');
+ if (comma == NULL)
+ goto bail;
+ *comma = '\0';
+ port_hi = bb_strtou(&comma[1], NULL, 10);
+ if (errno || port_hi > 0xff)
+ goto bail;
+ port |= port_hi << 8;
+
+#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
+ replace_char(raw, ',', '.');
+
+ /* We are verifying that PORT's IP matches getpeername().
+ * Otherwise peer can make us open data connections
+ * to other hosts (security problem!)
+ * This code would be too simplistic:
+ * lsa = xdotted2sockaddr(raw, port);
+ * if (lsa == NULL) goto bail;
+ */
+ if (!inet_aton(raw, &port_ipv4_sin_addr))
+ goto bail;
+ peer_ipv4_len = sizeof(peer_ipv4);
+ if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
+ goto bail;
+ if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
+ goto bail;
+
+ G.port_addr = xdotted2sockaddr(raw, port);
+#else
+ G.port_addr = get_peer_lsa(STDIN_FILENO);
+ set_nport(G.port_addr, htons(port));
+#endif
+ WRITE_OK(FTP_PORTOK);
+}
+
+static void
+handle_rest(void)
+{
+ /* When ftp_arg == NULL simply restart from beginning */
+ G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
+ WRITE_OK(FTP_RESTOK);
+}
+
+static void
+handle_retr(void)
+{
+ struct stat statbuf;
+ off_t bytes_transferred;
+ int remote_fd;
+ int local_file_fd;
+ off_t offset = G.restart_pos;
+ char *response;
+
+ G.restart_pos = 0;
+
+ if (!port_or_pasv_was_seen())
+ return; /* port_or_pasv_was_seen emitted error response */
+
+ /* O_NONBLOCK is useful if file happens to be a device node */
+ local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
+ if (local_file_fd < 0) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+
+ if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
+ /* Note - pretend open failed */
+ WRITE_ERR(FTP_FILEFAIL);
+ goto file_close_out;
+ }
+ G.local_file_fd = local_file_fd;
+
+ /* Now deactive O_NONBLOCK, otherwise we have a problem
+ * on DMAPI filesystems such as XFS DMAPI.
+ */
+ ndelay_off(local_file_fd);
+
+ /* Set the download offset (from REST) if any */
+ if (offset != 0)
+ xlseek(local_file_fd, offset, SEEK_SET);
+
+ response = xasprintf(
+ " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
+ G.ftp_arg, statbuf.st_size);
+ remote_fd = get_remote_transfer_fd(response);
+ free(response);
+ if (remote_fd < 0)
+ goto file_close_out;
+
+ bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
+ close(remote_fd);
+ if (bytes_transferred < 0)
+ WRITE_ERR(FTP_BADSENDFILE);
+ else
+ WRITE_OK(FTP_TRANSFEROK);
+
+ file_close_out:
+ close(local_file_fd);
+ G.local_file_fd = 0;
+}
+
+/* List commands */
+
+static int
+popen_ls(const char *opt)
+{
+ char *cwd;
+ const char *argv[] = {
+ "ftpd",
+ opt,
+ BB_MMU ? "--" : NULL,
+ G.ftp_arg,
+ NULL
+ };
+ struct fd_pair outfd;
+ pid_t pid;
+
+ cwd = xrealloc_getcwd_or_warn(NULL);
+ xpiped_pair(outfd);
+
+ /*fflush(NULL); - so far we dont use stdio on output */
+ pid = BB_MMU ? fork() : vfork();
+ if (pid < 0)
+ bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
+
+ if (pid == 0) {
+ /* child */
+#if !BB_MMU
+ if (fchdir(G.root_fd) != 0)
+ _exit(127);
+ close(G.root_fd);
+#endif
+ /* NB: close _first_, then move fd! */
+ close(outfd.rd);
+ xmove_fd(outfd.wr, STDOUT_FILENO);
+ /* Opening /dev/null in chroot is hard.
+ * Just making sure STDIN_FILENO is opened
+ * to something harmless. Paranoia,
+ * ls won't read it anyway */
+ close(STDIN_FILENO);
+ dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */
+#if !BB_MMU
+ /* ftpd ls helper chdirs to argv[2],
+ * preventing peer from seeing real root we are in now
+ */
+ argv[2] = cwd;
+ /* + 1: we must use relative path here if in chroot.
+ * For example, execv("/proc/self/exe") will fail, since
+ * it looks for "/proc/self/exe" _relative to chroot!_ */
+ execv(bb_busybox_exec_path + 1, (char**) argv);
+ _exit(127);
+#else
+ memset(&G, 0, sizeof(G));
+ exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv));
+#endif
+ }
+
+ /* parent */
+ close(outfd.wr);
+ free(cwd);
+ return outfd.rd;
+}
+
+enum {
+ USE_CTRL_CONN = 1,
+ LONG_LISTING = 2,
+};
+
+static void
+handle_dir_common(int opts)
+{
+ FILE *ls_fp;
+ char *line;
+ int ls_fd;
+
+ if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
+ return; /* port_or_pasv_was_seen emitted error response */
+
+ /* -n prevents user/groupname display,
+ * which can be problematic in chroot */
+ ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
+ ls_fp = fdopen(ls_fd, "r");
+ if (!ls_fp) /* never happens. paranoia */
+ bb_perror_msg_and_die("fdopen");
+
+ if (opts & USE_CTRL_CONN) {
+ /* STAT <filename> */
+ cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
+ while (1) {
+ line = xmalloc_fgetline(ls_fp);
+ if (!line)
+ break;
+ /* Hack: 0 results in no status at all */
+ /* Note: it's ok that we don't prepend space,
+ * ftp.kernel.org doesn't do that too */
+ cmdio_write(0, line);
+ free(line);
+ }
+ WRITE_OK(FTP_STATFILE_OK);
+ } else {
+ /* LIST/NLST [<filename>] */
+ int remote_fd = get_remote_transfer_fd(" Directory listing");
+ if (remote_fd >= 0) {
+ while (1) {
+ line = xmalloc_fgetline(ls_fp);
+ if (!line)
+ break;
+ /* I've seen clients complaining when they
+ * are fed with ls output with bare '\n'.
+ * Pity... that would be much simpler.
+ */
+/* TODO: need to s/LF/NUL/g here */
+ xwrite_str(remote_fd, line);
+ xwrite(remote_fd, "\r\n", 2);
+ free(line);
+ }
+ }
+ close(remote_fd);
+ WRITE_OK(FTP_TRANSFEROK);
+ }
+ fclose(ls_fp); /* closes ls_fd too */
+}
+static void
+handle_list(void)
+{
+ handle_dir_common(LONG_LISTING);
+}
+static void
+handle_nlst(void)
+{
+ /* NLST returns list of names, "\r\n" terminated without regard
+ * to the current binary flag. Names may start with "/",
+ * then they represent full names (we don't produce such names),
+ * otherwise names are relative to current directory.
+ * Embedded "\n" are replaced by NULs. This is safe since names
+ * can never contain NUL.
+ */
+ handle_dir_common(0);
+}
+static void
+handle_stat_file(void)
+{
+ handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
+}
+
+/* This can be extended to handle MLST, as all info is available
+ * in struct stat for that:
+ * MLST file_name
+ * 250-Listing file_name
+ * type=file;size=4161;modify=19970214165800; /dir/dir/file_name
+ * 250 End
+ * Nano-doc:
+ * MLST [<file or dir name, "." assumed if not given>]
+ * Returned name should be either the same as requested, or fully qualified.
+ * If there was no parameter, return "" or (preferred) fully-qualified name.
+ * Returned "facts" (case is not important):
+ * size - size in octets
+ * modify - last modification time
+ * type - entry type (file,dir,OS.unix=block)
+ * (+ cdir and pdir types for MLSD)
+ * unique - unique id of file/directory (inode#)
+ * perm -
+ * a: can be appended to (APPE)
+ * d: can be deleted (RMD/DELE)
+ * f: can be renamed (RNFR)
+ * r: can be read (RETR)
+ * w: can be written (STOR)
+ * e: can CWD into this dir
+ * l: this dir can be listed (dir only!)
+ * c: can create files in this dir
+ * m: can create dirs in this dir (MKD)
+ * p: can delete files in this dir
+ * UNIX.mode - unix file mode
+ */
+static void
+handle_size_or_mdtm(int need_size)
+{
+ struct stat statbuf;
+ struct tm broken_out;
+ char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
+ | sizeof("NNN YYYYMMDDhhmmss\r\n")
+ ];
+
+ if (!G.ftp_arg
+ || stat(G.ftp_arg, &statbuf) != 0
+ || !S_ISREG(statbuf.st_mode)
+ ) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+ if (need_size) {
+ sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
+ } else {
+ gmtime_r(&statbuf.st_mtime, &broken_out);
+ sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
+ broken_out.tm_year + 1900,
+ broken_out.tm_mon,
+ broken_out.tm_mday,
+ broken_out.tm_hour,
+ broken_out.tm_min,
+ broken_out.tm_sec);
+ }
+ cmdio_write_raw(buf);
+}
+
+/* Upload commands */
+
+#if ENABLE_FEATURE_FTP_WRITE
+static void
+handle_mkd(void)
+{
+ if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+ WRITE_OK(FTP_MKDIROK);
+}
+
+static void
+handle_rmd(void)
+{
+ if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+ WRITE_OK(FTP_RMDIROK);
+}
+
+static void
+handle_dele(void)
+{
+ if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+ WRITE_OK(FTP_DELEOK);
+}
+
+static void
+handle_rnfr(void)
+{
+ free(G.rnfr_filename);
+ G.rnfr_filename = xstrdup(G.ftp_arg);
+ WRITE_OK(FTP_RNFROK);
+}
+
+static void
+handle_rnto(void)
+{
+ int retval;
+
+ /* If we didn't get a RNFR, throw a wobbly */
+ if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
+ cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
+ return;
+ }
+
+ retval = rename(G.rnfr_filename, G.ftp_arg);
+ free(G.rnfr_filename);
+ G.rnfr_filename = NULL;
+
+ if (retval) {
+ WRITE_ERR(FTP_FILEFAIL);
+ return;
+ }
+ WRITE_OK(FTP_RENAMEOK);
+}
+
+static void
+handle_upload_common(int is_append, int is_unique)
+{
+ struct stat statbuf;
+ char *tempname;
+ off_t bytes_transferred;
+ off_t offset;
+ int local_file_fd;
+ int remote_fd;
+
+ offset = G.restart_pos;
+ G.restart_pos = 0;
+
+ if (!port_or_pasv_was_seen())
+ return; /* port_or_pasv_was_seen emitted error response */
+
+ tempname = NULL;
+ local_file_fd = -1;
+ if (is_unique) {
+ tempname = xstrdup(" FILE: uniq.XXXXXX");
+ local_file_fd = mkstemp(tempname + 7);
+ } else if (G.ftp_arg) {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC;
+ if (is_append)
+ flags = O_WRONLY | O_CREAT | O_APPEND;
+ if (offset)
+ flags = O_WRONLY | O_CREAT;
+ local_file_fd = open(G.ftp_arg, flags, 0666);
+ }
+
+ if (local_file_fd < 0
+ || fstat(local_file_fd, &statbuf) != 0
+ || !S_ISREG(statbuf.st_mode)
+ ) {
+ WRITE_ERR(FTP_UPLOADFAIL);
+ if (local_file_fd >= 0)
+ goto close_local_and_bail;
+ return;
+ }
+ G.local_file_fd = local_file_fd;
+
+ if (offset)
+ xlseek(local_file_fd, offset, SEEK_SET);
+
+ remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
+ free(tempname);
+
+ if (remote_fd < 0)
+ goto close_local_and_bail;
+
+ bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
+ close(remote_fd);
+ if (bytes_transferred < 0)
+ WRITE_ERR(FTP_BADSENDFILE);
+ else
+ WRITE_OK(FTP_TRANSFEROK);
+
+ close_local_and_bail:
+ close(local_file_fd);
+ G.local_file_fd = 0;
+}
+
+static void
+handle_stor(void)
+{
+ handle_upload_common(0, 0);
+}
+
+static void
+handle_appe(void)
+{
+ G.restart_pos = 0;
+ handle_upload_common(1, 0);
+}
+
+static void
+handle_stou(void)
+{
+ G.restart_pos = 0;
+ handle_upload_common(0, 1);
+}
+#endif /* ENABLE_FEATURE_FTP_WRITE */
+
+static uint32_t
+cmdio_get_cmd_and_arg(void)
+{
+ size_t len;
+ uint32_t cmdval;
+ char *cmd;
+
+ alarm(G.timeout);
+
+ free(G.ftp_cmd);
+ len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
+ G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len);
+ if (!cmd)
+ exit(0);
+
+ /* De-escape telnet: 0xff,0xff => 0xff */
+ /* RFC959 says that ABOR, STAT, QUIT may be sent even during
+ * data transfer, and may be preceded by telnet's "Interrupt Process"
+ * code (two-byte sequence 255,244) and then by telnet "Synch" code
+ * 255,242 (byte 242 is sent with TCP URG bit using send(MSG_OOB)
+ * and may generate SIGURG on our side. See RFC854).
+ * So far we don't support that (may install SIGURG handler if we'd want to),
+ * but we need to at least remove 255,xxx pairs. lftp sends those. */
+ /* Then de-escape FTP: NUL => '\n' */
+ /* Testing for \xff:
+ * Create file named '\xff': echo Hello >`echo -ne "\xff"`
+ * Try to get it: ftpget -v 127.0.0.1 Eff `echo -ne "\xff\xff"`
+ * (need "\xff\xff" until ftpget applet is fixed to do escaping :)
+ * Testing for embedded LF:
+ * LF_HERE=`echo -ne "LF\nHERE"`
+ * echo Hello >"$LF_HERE"
+ * ftpget -v 127.0.0.1 LF_HERE "$LF_HERE"
+ */
+ {
+ int dst, src;
+
+ /* Strip "\r\n" if it is there */
+ if (len != 0 && cmd[len - 1] == '\n') {
+ len--;
+ if (len != 0 && cmd[len - 1] == '\r')
+ len--;
+ cmd[len] = '\0';
+ }
+ src = strchrnul(cmd, 0xff) - cmd;
+ /* 99,99% there are neither NULs nor 255s and src == len */
+ if (src < len) {
+ dst = src;
+ do {
+ if ((unsigned char)(cmd[src]) == 255) {
+ src++;
+ /* 255,xxx - skip 255 */
+ if ((unsigned char)(cmd[src]) != 255) {
+ /* 255,!255 - skip both */
+ src++;
+ continue;
+ }
+ /* 255,255 - retain one 255 */
+ }
+ /* NUL => '\n' */
+ cmd[dst++] = cmd[src] ? cmd[src] : '\n';
+ src++;
+ } while (src < len);
+ cmd[dst] = '\0';
+ }
+ }
+
+ if (G.verbose > 1)
+ verbose_log(cmd);
+
+ G.ftp_arg = strchr(cmd, ' ');
+ if (G.ftp_arg != NULL)
+ *G.ftp_arg++ = '\0';
+
+ /* Uppercase and pack into uint32_t first word of the command */
+ cmdval = 0;
+ while (*cmd)
+ cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
+
+ return cmdval;
+}
+
+#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
+#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
+enum {
+ const_ALLO = mk_const4('A', 'L', 'L', 'O'),
+ const_APPE = mk_const4('A', 'P', 'P', 'E'),
+ const_CDUP = mk_const4('C', 'D', 'U', 'P'),
+ const_CWD = mk_const3('C', 'W', 'D'),
+ const_DELE = mk_const4('D', 'E', 'L', 'E'),
+ const_EPSV = mk_const4('E', 'P', 'S', 'V'),
+ const_FEAT = mk_const4('F', 'E', 'A', 'T'),
+ const_HELP = mk_const4('H', 'E', 'L', 'P'),
+ const_LIST = mk_const4('L', 'I', 'S', 'T'),
+ const_MDTM = mk_const4('M', 'D', 'T', 'M'),
+ const_MKD = mk_const3('M', 'K', 'D'),
+ const_MODE = mk_const4('M', 'O', 'D', 'E'),
+ const_NLST = mk_const4('N', 'L', 'S', 'T'),
+ const_NOOP = mk_const4('N', 'O', 'O', 'P'),
+ const_PASS = mk_const4('P', 'A', 'S', 'S'),
+ const_PASV = mk_const4('P', 'A', 'S', 'V'),
+ const_PORT = mk_const4('P', 'O', 'R', 'T'),
+ const_PWD = mk_const3('P', 'W', 'D'),
+ const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
+ const_REST = mk_const4('R', 'E', 'S', 'T'),
+ const_RETR = mk_const4('R', 'E', 'T', 'R'),
+ const_RMD = mk_const3('R', 'M', 'D'),
+ const_RNFR = mk_const4('R', 'N', 'F', 'R'),
+ const_RNTO = mk_const4('R', 'N', 'T', 'O'),
+ const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
+ const_STAT = mk_const4('S', 'T', 'A', 'T'),
+ const_STOR = mk_const4('S', 'T', 'O', 'R'),
+ const_STOU = mk_const4('S', 'T', 'O', 'U'),
+ const_STRU = mk_const4('S', 'T', 'R', 'U'),
+ const_SYST = mk_const4('S', 'Y', 'S', 'T'),
+ const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
+ const_USER = mk_const4('U', 'S', 'E', 'R'),
+
+#if !BB_MMU
+ OPT_l = (1 << 0),
+ OPT_1 = (1 << 1),
+#endif
+ OPT_v = (1 << ((!BB_MMU) * 2 + 0)),
+ OPT_S = (1 << ((!BB_MMU) * 2 + 1)),
+ OPT_w = (1 << ((!BB_MMU) * 2 + 2)) * ENABLE_FEATURE_FTP_WRITE,
+};
+
+int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+#if !BB_MMU
+int ftpd_main(int argc, char **argv)
+#else
+int ftpd_main(int argc UNUSED_PARAM, char **argv)
+#endif
+{
+ unsigned abs_timeout;
+ smallint opts;
+
+ INIT_G();
+
+ abs_timeout = 1 * 60 * 60;
+ G.timeout = 2 * 60;
+ opt_complementary = "t+:T+:vv";
+#if BB_MMU
+ opts = getopt32(argv, "vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
+#else
+ opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
+ if (opts & (OPT_l|OPT_1)) {
+ /* Our secret backdoor to ls */
+/* TODO: pass -n too? */
+/* --group-directories-first would be nice, but ls don't do that yet */
+ xchdir(argv[2]);
+ argv[2] = (char*)"--";
+ memset(&G, 0, sizeof(G));
+ return ls_main(argc, argv);
+ }
+#endif
+ if (abs_timeout | G.timeout) {
+ if (abs_timeout == 0)
+ abs_timeout = INT_MAX;
+ G.end_time = monotonic_sec() + abs_timeout;
+ if (G.timeout > abs_timeout)
+ G.timeout = abs_timeout;
+ }
+ strcpy(G.msg_ok + 4, MSG_OK );
+ strcpy(G.msg_err + 4, MSG_ERR);
+
+ G.local_addr = get_sock_lsa(STDIN_FILENO);
+ if (!G.local_addr) {
+ /* This is confusing:
+ * bb_error_msg_and_die("stdin is not a socket");
+ * Better: */
+ bb_show_usage();
+ /* Help text says that ftpd must be used as inetd service,
+ * which is by far the most usual cause of get_sock_lsa
+ * failure */
+ }
+
+ if (!(opts & OPT_v))
+ logmode = LOGMODE_NONE;
+ if (opts & OPT_S) {
+ /* LOG_NDELAY is needed since we may chroot later */
+ openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ logmode |= LOGMODE_SYSLOG;
+ }
+ if (logmode)
+ applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
+
+#if !BB_MMU
+ G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
+#endif
+
+ if (argv[optind]) {
+ xchdir(argv[optind]);
+ chroot(".");
+ }
+
+ //umask(077); - admin can set umask before starting us
+
+ /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
+ signal(SIGPIPE, SIG_IGN);
+
+ /* Set up options on the command socket (do we need these all? why?) */
+ setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
+ setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ /* Telnet protocol over command link may send "urgent" data,
+ * we prefer it to be received in the "normal" data stream: */
+ setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
+
+ WRITE_OK(FTP_GREET);
+ signal(SIGALRM, timeout_handler);
+
+#ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
+ {
+ smallint user_was_specified = 0;
+ while (1) {
+ uint32_t cmdval = cmdio_get_cmd_and_arg();
+
+ if (cmdval == const_USER) {
+ if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
+ cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
+ else {
+ user_was_specified = 1;
+ cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
+ }
+ } else if (cmdval == const_PASS) {
+ if (user_was_specified)
+ break;
+ cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
+ } else if (cmdval == const_QUIT) {
+ WRITE_OK(FTP_GOODBYE);
+ return 0;
+ } else {
+ cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
+ }
+ }
+ }
+ WRITE_OK(FTP_LOGINOK);
+#endif
+
+ /* RFC-959 Section 5.1
+ * The following commands and options MUST be supported by every
+ * server-FTP and user-FTP, except in cases where the underlying
+ * file system or operating system does not allow or support
+ * a particular command.
+ * Type: ASCII Non-print, IMAGE, LOCAL 8
+ * Mode: Stream
+ * Structure: File, Record*
+ * (Record structure is REQUIRED only for hosts whose file
+ * systems support record structure).
+ * Commands:
+ * USER, PASS, ACCT, [bbox: ACCT not supported]
+ * PORT, PASV,
+ * TYPE, MODE, STRU,
+ * RETR, STOR, APPE,
+ * RNFR, RNTO, DELE,
+ * CWD, CDUP, RMD, MKD, PWD,
+ * LIST, NLST,
+ * SYST, STAT,
+ * HELP, NOOP, QUIT.
+ */
+ /* ACCOUNT (ACCT)
+ * "The argument field is a Telnet string identifying the user's account.
+ * The command is not necessarily related to the USER command, as some
+ * sites may require an account for login and others only for specific
+ * access, such as storing files. In the latter case the command may
+ * arrive at any time.
+ * There are reply codes to differentiate these cases for the automation:
+ * when account information is required for login, the response to
+ * a successful PASSword command is reply code 332. On the other hand,
+ * if account information is NOT required for login, the reply to
+ * a successful PASSword command is 230; and if the account information
+ * is needed for a command issued later in the dialogue, the server
+ * should return a 332 or 532 reply depending on whether it stores
+ * (pending receipt of the ACCounT command) or discards the command,
+ * respectively."
+ */
+
+ while (1) {
+ uint32_t cmdval = cmdio_get_cmd_and_arg();
+
+ if (cmdval == const_QUIT) {
+ WRITE_OK(FTP_GOODBYE);
+ return 0;
+ }
+ else if (cmdval == const_USER)
+ /* This would mean "ok, now give me PASS". */
+ /*WRITE_OK(FTP_GIVEPWORD);*/
+ /* vsftpd can be configured to not require that,
+ * and this also saves one roundtrip:
+ */
+ WRITE_OK(FTP_LOGINOK);
+ else if (cmdval == const_PASS)
+ WRITE_OK(FTP_LOGINOK);
+ else if (cmdval == const_NOOP)
+ WRITE_OK(FTP_NOOPOK);
+ else if (cmdval == const_TYPE)
+ WRITE_OK(FTP_TYPEOK);
+ else if (cmdval == const_STRU)
+ WRITE_OK(FTP_STRUOK);
+ else if (cmdval == const_MODE)
+ WRITE_OK(FTP_MODEOK);
+ else if (cmdval == const_ALLO)
+ WRITE_OK(FTP_ALLOOK);
+ else if (cmdval == const_SYST)
+ cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
+ else if (cmdval == const_PWD)
+ handle_pwd();
+ else if (cmdval == const_CWD)
+ handle_cwd();
+ else if (cmdval == const_CDUP) /* cd .. */
+ handle_cdup();
+ /* HELP is nearly useless, but we can reuse FEAT for it */
+ /* lftp uses FEAT */
+ else if (cmdval == const_HELP || cmdval == const_FEAT)
+ handle_feat(cmdval == const_HELP
+ ? STRNUM32(FTP_HELP)
+ : STRNUM32(FTP_STATOK)
+ );
+ else if (cmdval == const_LIST) /* ls -l */
+ handle_list();
+ else if (cmdval == const_NLST) /* "name list", bare ls */
+ handle_nlst();
+ /* SIZE is crucial for wget's download indicator etc */
+ /* Mozilla, lftp use MDTM (presumably for caching) */
+ else if (cmdval == const_SIZE || cmdval == const_MDTM)
+ handle_size_or_mdtm(cmdval == const_SIZE);
+ else if (cmdval == const_STAT) {
+ if (G.ftp_arg == NULL)
+ handle_stat();
+ else
+ handle_stat_file();
+ }
+ else if (cmdval == const_PASV)
+ handle_pasv();
+ else if (cmdval == const_EPSV)
+ handle_epsv();
+ else if (cmdval == const_RETR)
+ handle_retr();
+ else if (cmdval == const_PORT)
+ handle_port();
+ else if (cmdval == const_REST)
+ handle_rest();
+#if ENABLE_FEATURE_FTP_WRITE
+ else if (opts & OPT_w) {
+ if (cmdval == const_STOR)
+ handle_stor();
+ else if (cmdval == const_MKD)
+ handle_mkd();
+ else if (cmdval == const_RMD)
+ handle_rmd();
+ else if (cmdval == const_DELE)
+ handle_dele();
+ else if (cmdval == const_RNFR) /* "rename from" */
+ handle_rnfr();
+ else if (cmdval == const_RNTO) /* "rename to" */
+ handle_rnto();
+ else if (cmdval == const_APPE)
+ handle_appe();
+ else if (cmdval == const_STOU) /* "store unique" */
+ handle_stou();
+ else
+ goto bad_cmd;
+ }
+#endif
+#if 0
+ else if (cmdval == const_STOR
+ || cmdval == const_MKD
+ || cmdval == const_RMD
+ || cmdval == const_DELE
+ || cmdval == const_RNFR
+ || cmdval == const_RNTO
+ || cmdval == const_APPE
+ || cmdval == const_STOU
+ ) {
+ cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
+ }
+#endif
+ else {
+ /* Which unsupported commands were seen in the wild?
+ * (doesn't necessarily mean "we must support them")
+ * foo 1.2.3: XXXX - comment
+ */
+#if ENABLE_FEATURE_FTP_WRITE
+ bad_cmd:
+#endif
+ cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
+ }
+ }
+}
diff --git a/release/src/router/busybox/networking/ftpgetput.c b/release/src/router/busybox/networking/ftpgetput.c
index 41f45414..d39b73eb 100644
--- a/release/src/router/busybox/networking/ftpgetput.c
+++ b/release/src/router/busybox/networking/ftpgetput.c
@@ -1,183 +1,208 @@
/* vi: set sw=4 ts=4: */
/*
- * ftpget
- *
+ * ftpget
+ *
* Mini implementation of FTP to retrieve a remote file.
*
* Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
- * Copyright (C) 2002 Glenn McGrath <bug1@optushome.com.au>
+ * Copyright (C) 2002 Glenn McGrath
*
* Based on wget.c by Chip Rosenthal Covad Communications
* <chip@laserlink.net>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include "busybox.h"
-
-typedef struct ftp_host_info_s {
- char *host;
- char *port;
- char *user;
- char *password;
-} ftp_host_info_t;
-
-static char verbose_flag;
-static char do_continue = 0;
-
-static ftp_host_info_t *ftp_init(void)
+#include "libbb.h"
+
+struct globals {
+ const char *user;
+ const char *password;
+ struct len_and_sockaddr *lsa;
+ FILE *control_stream;
+ int verbose_flag;
+ int do_continue;
+ char buf[1]; /* actually [BUFSZ] */
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
+struct BUG_G_too_big {
+ char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
+#define user (G.user )
+#define password (G.password )
+#define lsa (G.lsa )
+#define control_stream (G.control_stream)
+#define verbose_flag (G.verbose_flag )
+#define do_continue (G.do_continue )
+#define buf (G.buf )
+#define INIT_G() do { } while (0)
+
+
+static void ftp_die(const char *msg) NORETURN;
+static void ftp_die(const char *msg)
{
- ftp_host_info_t *host;
+ char *cp = buf; /* buf holds peer's response */
+
+ /* Guard against garbage from remote server */
+ while (*cp >= ' ' && *cp < '\x7f')
+ cp++;
+ *cp = '\0';
+ bb_error_msg_and_die("unexpected server response%s%s: %s",
+ (msg ? " to " : ""), (msg ? msg : ""), buf);
+}
+
+static int ftpcmd(const char *s1, const char *s2)
+{
+ unsigned n;
- host = xcalloc(1, sizeof(ftp_host_info_t));
+ if (verbose_flag) {
+ bb_error_msg("cmd %s %s", s1, s2);
+ }
- /* Set the default port */
- if (getservbyname("ftp", "tcp")) {
- host->port = "ftp";
- } else {
- host->port = "21";
+ if (s1) {
+ fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
+ s1, s2);
+ fflush(control_stream);
}
- host->user = "anonymous";
- host->password = "busybox@";
- return(host);
+ do {
+ strcpy(buf, "EOF");
+ if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
+ ftp_die(NULL);
+ }
+ } while (!isdigit(buf[0]) || buf[3] != ' ');
+
+ buf[3] = '\0';
+ n = xatou(buf);
+ buf[3] = ' ';
+ return n;
}
-static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
+static void ftp_login(void)
{
- if (verbose_flag) {
- bb_error_msg("cmd %s%s", s1, s2);
+ /* Connect to the command socket */
+ control_stream = fdopen(xconnect_stream(lsa), "r+");
+ if (control_stream == NULL) {
+ /* fdopen failed - extremely unlikely */
+ bb_perror_nomsg_and_die();
}
- if (s1) {
- if (s2) {
- fprintf(stream, "%s%s\n", s1, s2);
- } else {
- fprintf(stream, "%s\n", s1);
- }
+ if (ftpcmd(NULL, NULL) != 220) {
+ ftp_die(NULL);
}
- do {
- if (fgets(buf, 510, stream) == NULL) {
- bb_perror_msg_and_die("fgets()");
+ /* Login to the server */
+ switch (ftpcmd("USER", user)) {
+ case 230:
+ break;
+ case 331:
+ if (ftpcmd("PASS", password) != 230) {
+ ftp_die("PASS");
}
- } while (! isdigit(buf[0]) || buf[3] != ' ');
+ break;
+ default:
+ ftp_die("USER");
+ }
- return atoi(buf);
+ ftpcmd("TYPE I", NULL);
}
-static int xconnect_ftpdata(const char *target_host, const char *buf)
+static int xconnect_ftpdata(void)
{
char *buf_ptr;
- char data_port[6];
- unsigned short port_num;
+ unsigned port_num;
+
+/*
+TODO: PASV command will not work for IPv6. RFC2428 describes
+IPv6-capable "extended PASV" - EPSV.
+
+"EPSV [protocol]" asks server to bind to and listen on a data port
+in specified protocol. Protocol is 1 for IPv4, 2 for IPv6.
+If not specified, defaults to "same as used for control connection".
+If server understood you, it should answer "229 <some text>(|||port|)"
+where "|" are literal pipe chars and "port" is ASCII decimal port#.
+
+There is also an IPv6-capable replacement for PORT (EPRT),
+but we don't need that.
+
+NB: PASV may still work for some servers even over IPv6.
+For example, vsftp happily answers
+"227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual.
+
+TODO2: need to stop ignoring IP address in PASV response.
+*/
+
+ if (ftpcmd("PASV", NULL) != 227) {
+ ftp_die("PASV");
+ }
+
+ /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage]
+ * Server's IP is N1.N2.N3.N4 (we ignore it)
+ * Server's port for data connection is P1*256+P2 */
+ buf_ptr = strrchr(buf, ')');
+ if (buf_ptr) *buf_ptr = '\0';
buf_ptr = strrchr(buf, ',');
*buf_ptr = '\0';
- port_num = atoi(buf_ptr + 1);
+ port_num = xatoul_range(buf_ptr + 1, 0, 255);
buf_ptr = strrchr(buf, ',');
*buf_ptr = '\0';
- port_num += atoi(buf_ptr + 1) * 256;
+ port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
- sprintf(data_port, "%d", port_num);
- return(xconnect(target_host, data_port));
+ set_nport(lsa, htons(port_num));
+ return xconnect_stream(lsa);
}
-static FILE *ftp_login(ftp_host_info_t *server)
+static int pump_data_and_QUIT(int from, int to)
{
- FILE *control_stream;
- char buf[512];
- int control_fd;
-
- /* Connect to the command socket */
- control_fd = xconnect(server->host, server->port);
- control_stream = fdopen(control_fd, "r+");
- if (control_stream == NULL) {
- bb_perror_msg_and_die("Couldnt open control stream");
+ /* copy the file */
+ if (bb_copyfd_eof(from, to) == -1) {
+ /* error msg is already printed by bb_copyfd_eof */
+ return EXIT_FAILURE;
}
- if (ftpcmd(NULL, NULL, control_stream, buf) != 220) {
- bb_error_msg_and_die("%s", buf + 4);
- }
+ /* close data connection */
+ close(from); /* don't know which one is that, so we close both */
+ close(to);
- /* Login to the server */
- switch (ftpcmd("USER ", server->user, control_stream, buf)) {
- case 230:
- break;
- case 331:
- if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) {
- bb_error_msg_and_die("PASS error: %s", buf + 4);
- }
- break;
- default:
- bb_error_msg_and_die("USER error: %s", buf + 4);
+ /* does server confirm that transfer is finished? */
+ if (ftpcmd(NULL, NULL) != 226) {
+ ftp_die(NULL);
}
+ ftpcmd("QUIT", NULL);
- ftpcmd("TYPE I", NULL, control_stream, buf);
-
- return(control_stream);
+ return EXIT_SUCCESS;
}
-#ifdef CONFIG_FTPGET
-static int ftp_recieve(FILE *control_stream, const char *host, const char *local_path, char *server_path)
+#if !ENABLE_FTPGET
+int ftp_receive(const char *local_path, char *server_path);
+#else
+static
+int ftp_receive(const char *local_path, char *server_path)
{
- char *filename;
- char *local_file;
- char buf[512];
- off_t filesize = 0;
int fd_data;
- int fd_local;
+ int fd_local = -1;
off_t beg_range = 0;
- filename = bb_get_last_path_component(server_path);
- local_file = concat_path_file(local_path, filename);
+ /* connect to the data socket */
+ fd_data = xconnect_ftpdata();
- /* Connect to the data socket */
- if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
- bb_error_msg_and_die("PASV error: %s", buf + 4);
+ if (ftpcmd("SIZE", server_path) != 213) {
+ do_continue = 0;
}
- fd_data = xconnect_ftpdata(host, buf);
- if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) {
- filesize = atol(buf + 4);
+ if (LONE_DASH(local_path)) {
+ fd_local = STDOUT_FILENO;
+ do_continue = 0;
}
if (do_continue) {
struct stat sbuf;
- if (lstat(local_file, &sbuf) < 0) {
- bb_perror_msg_and_die("fstat()");
+ /* lstat would be wrong here! */
+ if (stat(local_path, &sbuf) < 0) {
+ bb_perror_msg_and_die("stat");
}
if (sbuf.st_size > 0) {
beg_range = sbuf.st_size;
@@ -187,183 +212,114 @@ static int ftp_recieve(FILE *control_stream, const char *host, const char *local
}
if (do_continue) {
- sprintf(buf, "REST %ld", (long)beg_range);
- if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
+ sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+ if (ftpcmd(buf, NULL) != 350) {
do_continue = 0;
- } else {
- filesize -= beg_range;
}
}
- if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) {
- bb_error_msg_and_die("RETR error: %s", buf + 4);
- }
-
- /* only make a local file if we know that one exists on the remote server */
- if (do_continue) {
- fd_local = bb_xopen(local_file, O_APPEND | O_WRONLY);
- } else {
- fd_local = bb_xopen(local_file, O_CREAT | O_TRUNC | O_WRONLY);
+ if (ftpcmd("RETR", server_path) > 150) {
+ ftp_die("RETR");
}
- /* Copy the file */
- if (bb_copyfd(fd_data, fd_local, filesize) == -1) {
- exit(EXIT_FAILURE);
+ /* create local file _after_ we know that remote file exists */
+ if (fd_local == -1) {
+ fd_local = xopen(local_path,
+ do_continue ? (O_APPEND | O_WRONLY)
+ : (O_CREAT | O_TRUNC | O_WRONLY)
+ );
}
- /* close it all down */
- close(fd_data);
- if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
- bb_error_msg_and_die("ftp error: %s", buf + 4);
- }
- ftpcmd("QUIT", NULL, control_stream, buf);
-
- return(EXIT_SUCCESS);
+ return pump_data_and_QUIT(fd_data, fd_local);
}
#endif
-#ifdef CONFIG_FTPPUT
-static int ftp_send(FILE *control_stream, const char *host, const char *server_path, char *local_path)
+#if !ENABLE_FTPPUT
+int ftp_send(const char *server_path, char *local_path);
+#else
+static
+int ftp_send(const char *server_path, char *local_path)
{
- struct stat sbuf;
- char buf[512];
int fd_data;
int fd_local;
int response;
- /* Connect to the data socket */
- if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
- bb_error_msg_and_die("PASV error: %s", buf + 4);
- }
- fd_data = xconnect_ftpdata(host, buf);
-
- if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) {
- bb_error_msg_and_die("CWD error: %s", buf + 4);
- }
+ /* connect to the data socket */
+ fd_data = xconnect_ftpdata();
/* get the local file */
- fd_local = bb_xopen(local_path, O_RDONLY);
- fstat(fd_local, &sbuf);
-
- sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size);
- response = ftpcmd(buf, NULL, control_stream, buf);
- switch (response) {
- case 200:
- case 202:
- break;
- default:
- close(fd_local);
- bb_error_msg_and_die("ALLO error: %s", buf + 4);
- break;
- }
+ fd_local = STDIN_FILENO;
+ if (NOT_LONE_DASH(local_path))
+ fd_local = xopen(local_path, O_RDONLY);
- response = ftpcmd("STOR ", local_path, control_stream, buf);
+ response = ftpcmd("STOR", server_path);
switch (response) {
case 125:
case 150:
break;
default:
- close(fd_local);
- bb_error_msg_and_die("STOR error: %s", buf + 4);
- }
-
- /* transfer the file */
- if (bb_copyfd(fd_local, fd_data, 0) == -1) {
- exit(EXIT_FAILURE);
+ ftp_die("STOR");
}
- /* close it all down */
- close(fd_data);
- if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
- bb_error_msg_and_die("error: %s", buf + 4);
- }
- ftpcmd("QUIT", NULL, control_stream, buf);
-
- return(EXIT_SUCCESS);
+ return pump_data_and_QUIT(fd_local, fd_data);
}
#endif
-int ftpgetput_main(int argc, char **argv)
-{
- /* content-length of the file */
- int option_index = -1;
- int opt;
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+static const char ftpgetput_longopts[] ALIGN1 =
+ "continue\0" Required_argument "c"
+ "verbose\0" No_argument "v"
+ "username\0" Required_argument "u"
+ "password\0" Required_argument "p"
+ "port\0" Required_argument "P"
+ ;
+#endif
+int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opt;
+ const char *port = "ftp";
/* socket to ftp server */
- FILE *control_stream;
-
- /* continue a prev transfer (-c) */
- ftp_host_info_t *server;
-
- int (*ftp_action)(FILE *, const char *, const char *, char *) = NULL;
- struct option long_options[] = {
- {"username", 1, NULL, 'u'},
- {"password", 1, NULL, 'p'},
- {"port", 1, NULL, 'P'},
- {"continue", 1, NULL, 'c'},
- {"verbose", 0, NULL, 'v'},
- {0, 0, 0, 0}
- };
+#if ENABLE_FTPPUT && !ENABLE_FTPGET
+# define ftp_action ftp_send
+#elif ENABLE_FTPGET && !ENABLE_FTPPUT
+# define ftp_action ftp_receive
+#else
+ int (*ftp_action)(const char *, char *) = ftp_send;
-#ifdef CONFIG_FTPPUT
- if (bb_applet_name[3] == 'p') {
- ftp_action = ftp_send;
- }
-#endif
-#ifdef CONFIG_FTPGET
- if (bb_applet_name[3] == 'g') {
- ftp_action = ftp_recieve;
+ /* Check to see if the command is ftpget or ftput */
+ if (applet_name[3] == 'g') {
+ ftp_action = ftp_receive;
}
#endif
+ INIT_G();
/* Set default values */
- server = ftp_init();
- verbose_flag = 0;
-
- /*
- * Decipher the command line
- */
- while ((opt = getopt_long(argc, argv, "u:p:P:cv", long_options, &option_index)) != EOF) {
- switch(opt) {
- case 'c':
- do_continue = 1;
- break;
- case 'u':
- server->user = optarg;
- break;
- case 'p':
- server->password = optarg;
- break;
- case 'P':
- server->port = optarg;
- break;
- case 'v':
- verbose_flag = 1;
- break;
- default:
- bb_show_usage();
- }
- }
+ user = "anonymous";
+ password = "busybox@";
/*
- * Process the non-option command line arguments
+ * Decipher the command line
*/
- if (argc - optind != 3) {
- bb_show_usage();
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+ applet_long_options = ftpgetput_longopts;
+#endif
+ opt_complementary = "=3:vv:cc"; /* must have 3 params; -v and -c count */
+ opt = getopt32(argv, "cvu:p:P:", &user, &password, &port,
+ &verbose_flag, &do_continue);
+ argv += optind;
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
+ if (verbose_flag) {
+ printf("Connecting to %s (%s)\n", argv[0],
+ xmalloc_sockaddr2dotted(&lsa->u.sa));
}
- /* Connect/Setup/Configure the FTP session */
- server->host = argv[optind];
- control_stream = ftp_login(server);
-
- return(ftp_action(control_stream, argv[optind], argv[optind + 1], argv[optind + 2]));
+ ftp_login();
+ return ftp_action(argv[1], argv[2]);
}
-
-/*
-Local Variables:
-c-file-style: "linux"
-c-basic-offset: 4
-tab-width: 4
-End:
-*/
diff --git a/release/src/router/busybox/networking/hostname.c b/release/src/router/busybox/networking/hostname.c
index 11593e7a..48e70db9 100644
--- a/release/src/router/busybox/networking/hostname.c
+++ b/release/src/router/busybox/networking/hostname.c
@@ -1,6 +1,5 @@
/* vi: set sw=4 ts=4: */
/*
- * $Id: hostname.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $
* Mini hostname implementation for busybox
*
* Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
@@ -8,123 +7,87 @@
* adjusted by Erik Andersen <andersen@codepoet.org> to remove
* use of long options and GNU getopt. Improved the usage info.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <errno.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include "busybox.h"
-
-extern char *optarg; /* in unistd.h */
-extern int optind, opterr, optopt; /* in unistd.h */
+#include "libbb.h"
static void do_sethostname(char *s, int isfile)
{
- FILE *f;
- char buf[255];
-
if (!s)
return;
- if (!isfile) {
- if (sethostname(s, strlen(s)) < 0) {
- if (errno == EPERM)
- bb_error_msg_and_die("you must be root to change the hostname");
- else
- bb_perror_msg_and_die("sethostname");
+ if (isfile) {
+ parser_t *parser = config_open2(s, xfopen_for_read);
+ while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
+ do_sethostname(s, 0);
}
- } else {
- f = bb_xfopen(s, "r");
- while (fgets(buf, 255, f) != NULL) {
- if (buf[0] =='#') {
- continue;
- }
- chomp(buf);
- do_sethostname(buf, 0);
- }
-#ifdef CONFIG_FEATURE_CLEAN_UP
- fclose(f);
-#endif
+ if (ENABLE_FEATURE_CLEAN_UP)
+ config_close(parser);
+ } else if (sethostname(s, strlen(s)) < 0) {
+ if (errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ bb_perror_msg_and_die("sethostname");
}
}
+int hostname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int hostname_main(int argc, char **argv)
{
- int opt;
- int type = 0;
- struct hostent *hp;
- char *filename = NULL;
- char buf[255];
- char *p = NULL;
+ enum {
+ OPT_d = 0x1,
+ OPT_f = 0x2,
+ OPT_i = 0x4,
+ OPT_s = 0x8,
+ OPT_F = 0x10,
+ OPT_dfis = 0xf,
+ };
+
+ char *buf;
+ char *hostname_str;
if (argc < 1)
bb_show_usage();
- while ((opt = getopt(argc, argv, "dfisF:")) > 0) {
- switch (opt) {
- case 'd':
- case 'f':
- case 'i':
- case 's':
- type = opt;
- break;
- case 'F':
- filename = optarg;
- break;
- default:
- bb_show_usage();
- }
- }
+ getopt32(argv, "dfisF:", &hostname_str);
+ argv += optind;
+ buf = safe_gethostname();
/* Output in desired format */
- if (type != 0) {
- gethostname(buf, 255);
+ if (option_mask32 & OPT_dfis) {
+ struct hostent *hp;
+ char *p;
hp = xgethostbyname(buf);
p = strchr(hp->h_name, '.');
- if (type == 'f') {
+ if (option_mask32 & OPT_f) {
puts(hp->h_name);
- } else if (type == 's') {
- if (p != NULL) {
- *p = 0;
- }
+ } else if (option_mask32 & OPT_s) {
+ if (p)
+ *p = '\0';
puts(hp->h_name);
- } else if (type == 'd') {
- if (p) puts(p + 1);
- } else if (type == 'i') {
+ } else if (option_mask32 & OPT_d) {
+ if (p)
+ puts(p + 1);
+ } else if (option_mask32 & OPT_i) {
while (hp->h_addr_list[0]) {
printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++)));
}
- printf("\n");
+ bb_putchar('\n');
}
}
/* Set the hostname */
- else if (filename != NULL) {
- do_sethostname(filename, 1);
- } else if (optind < argc) {
- do_sethostname(argv[optind], 0);
+ else if (option_mask32 & OPT_F) {
+ do_sethostname(hostname_str, 1);
+ } else if (argv[0]) {
+ do_sethostname(argv[0], 0);
}
/* Or if all else fails,
* just print the current hostname */
- else {
- gethostname(buf, 255);
+ else {
puts(buf);
}
- return(0);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(buf);
+ return EXIT_SUCCESS;
}
diff --git a/release/src/router/busybox/networking/httpd.c b/release/src/router/busybox/networking/httpd.c
index d58414b5..ae911691 100644
--- a/release/src/router/busybox/networking/httpd.c
+++ b/release/src/router/busybox/networking/httpd.c
@@ -1,24 +1,13 @@
+/* vi: set sw=4 ts=4: */
/*
* httpd implementation for busybox
*
* Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
- * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
*
* simplify patch stolen from libbb without using strdup
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
*****************************************************************************
*
@@ -31,64 +20,62 @@
* httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
*
*
- * When a url contains "cgi-bin" it is assumed to be a cgi script. The
+ * When a url starts by "/cgi-bin/" it is assumed to be a cgi script. The
* server changes directory to the location of the script and executes it
- * after setting QUERY_STRING and other environment variables. If url args
- * are included in the url or as a post, the args are placed into decoded
- * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set
- * the $CGI_foo environment variable to "Hello World" while
- * CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV enabled.
+ * after setting QUERY_STRING and other environment variables.
*
- * The server can also be invoked as a url arg decoder and html text encoder
+ * Doc:
+ * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+ *
+ * The applet can also be invoked as a url arg decoder and html text encoder
* as follows:
* foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
* bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
* Note that url encoding for arguments is not the same as html encoding for
- * presenation. -d decodes a url-encoded argument while -e encodes in html
+ * presentation. -d decodes an url-encoded argument while -e encodes in html
* for page display.
*
* httpd.conf has the following format:
- *
- * A:172.20. # Allow any address that begins with 172.20
- * A:10.10. # Allow any address that begins with 10.10.
- * A:10.20 # Allow any address that previous set and 10.200-209.X.X
+ *
+ * H:/serverroot # define the server root. It will override -h
+ * A:172.20. # Allow address from 172.20.0.0/16
+ * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
+ * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
* A:127.0.0.1 # Allow local loopback connections
* D:* # Deny from other IP connections
+ * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
+ * I:index.html # Show index.html when a directory is requested
+ *
+ * P:/url:[http://]hostname[:port]/new/path
+ * # When /urlXXXXXX is requested, reverse proxy
+ * # it to http://hostname[:port]/new/pathXXXXXX
+ *
* /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
* /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
* /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
* .au:audio/basic # additional mime type for audio.au files
- *
- * A/D may be as a/d or allow/deny - first char case unsensitive
- * Deny IP rules take precedence over allow rules.
- *
- *
- * The Deny/Allow IP logic:
- *
- * - Default is to allow all. No addresses are denied unless
- * denied with a D: rule.
- * - Order of Deny/Allow rules is significant
+ * *.php:/path/php # run xxx.php through an interpreter
+ *
+ * A/D may be as a/d or allow/deny - only first char matters.
+ * Deny/Allow IP logic:
+ * - Default is to allow all (Allow all (A:*) is a no-op).
* - Deny rules take precedence over allow rules.
- * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
- * addresses.
- * - Specification of Allow all (A:*) is a no-op
- *
+ * - "Deny all" rule (D:*) is applied last.
+ *
* Example:
* 1. Allow only specified addresses
- * A:172.20. # Allow any address that begins with 172.20
+ * A:172.20 # Allow any address that begins with 172.20.
* A:10.10. # Allow any address that begins with 10.10.
- * A:10.10 # Allow any address that previous set and 10.100-109.X.X
* A:127.0.0.1 # Allow local loopback connections
* D:* # Deny from other IP connections
- *
+ *
* 2. Only deny specified addresses
* D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
* D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
* A:* # (optional line added for clarity)
- *
+ *
* If a sub directory contains a config file it is parsed and merged with
- * any existing settings as if it was appended to the original configuration
- * except that all previous IP config rules are discarded.
+ * any existing settings as if it was appended to the original configuration.
*
* subdir paths are relative to the containing subdir and thus cannot
* affect the parent rules.
@@ -97,1819 +84,2316 @@
* subdir http request, any merge is discarded when the process exits. As a
* result, the subdir settings only have a lifetime of a single request.
*
- *
- * If -c is not set, an attempt will be made to open the default
+ * Custom error pages can contain an absolute path or be relative to
+ * 'home_httpd'. Error pages are to be static files (no CGI or script). Error
+ * page can only be defined in the root configuration file and are not taken
+ * into account in local (directories) config files.
+ *
+ * If -c is not set, an attempt will be made to open the default
* root configuration file. If -c is set and the file is not found, the
* server exits with an error.
- *
-*/
-
+ *
+ */
+ /* TODO: use TCP_CORK, parse_config() */
-#include <stdio.h>
-#include <ctype.h> /* for isspace */
-#include <string.h>
-#include <stdlib.h> /* for malloc */
-#include <time.h>
-#include <unistd.h> /* for close */
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/socket.h> /* for connect and socket*/
-#include <netinet/in.h> /* for sockaddr_in */
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <fcntl.h> /* for open modes */
-#include "busybox.h"
-
-
-static const char httpdVersion[] = "busybox httpd/1.28 22-Jun-2003";
-static const char default_path_httpd_conf[] = "/etc";
-static const char httpd_conf[] = "httpd.conf";
-static const char home[] = "./";
-
-// Note: bussybox xfuncs are not used because we want the server to keep running
-// if something bad happens due to a malformed user request.
-// As a result, all memory allocation after daemonize
-// is checked rigorously
-
-//#define DEBUG 1
-
-/* Configure options, disabled by default as custom httpd feature */
-
-/* disabled as optional features */
-//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
-//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
-//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-//#define CONFIG_FEATURE_HTTPD_SETUID
-//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
-
-/* If set, use this server from internet superserver only */
-//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-
-/* You can use this server as standalone, require libbb.a for linking */
-//#define HTTPD_STANDALONE
-
-/* Config options, disable this for do very small module */
-//#define CONFIG_FEATURE_HTTPD_CGI
-//#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
-//#define CONFIG_FEATURE_HTTPD_AUTH_MD5
-
-#ifdef HTTPD_STANDALONE
-/* standalone, enable all features */
-#undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-/* unset config option for remove warning as redefined */
-#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-#undef CONFIG_FEATURE_HTTPD_AUTH_MD5
-#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
-#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
-#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-#undef CONFIG_FEATURE_HTTPD_CGI
-#undef CONFIG_FEATURE_HTTPD_SETUID
-#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
-/* enable all features now */
-#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
-#define CONFIG_FEATURE_HTTPD_AUTH_MD5
-#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
-#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
-#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-#define CONFIG_FEATURE_HTTPD_CGI
-#define CONFIG_FEATURE_HTTPD_SETUID
-#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
-
-/* require from libbb.a for linking */
-const char *bb_applet_name = "httpd";
-
-void bb_show_usage(void)
-{
- fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
- "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
- exit(1);
-}
+#include "libbb.h"
+#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+# include <sys/sendfile.h>
#endif
-#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-#undef CONFIG_FEATURE_HTTPD_SETUID /* use inetd user.group config settings */
-#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP /* so is not daemon */
-/* inetd set stderr to accepted socket and we can`t true see debug messages */
-#undef DEBUG
-#endif
+#define DEBUG 0
-/* CGI environ size */
-#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
-#define ENVSIZE 70 /* set max CGI variable */
-#else
-#define ENVSIZE 15 /* minimal requires */
+#define IOBUF_SIZE 8192 /* IO buffer */
+
+/* amount of buffering in a pipe */
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096
+#endif
+#if PIPE_BUF >= IOBUF_SIZE
+# error "PIPE_BUF >= IOBUF_SIZE"
#endif
-#define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */
+#define HEADER_READ_TIMEOUT 60
+
+static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
+static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
+static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";
-#define MAX_MEMORY_BUFF 8192 /* IO buffer */
+typedef struct has_next_ptr {
+ struct has_next_ptr *next;
+} has_next_ptr;
-typedef struct HT_ACCESS {
+/* Must have "next" as a first member */
+typedef struct Htaccess {
+ struct Htaccess *next;
char *after_colon;
- struct HT_ACCESS *next;
- char before_colon[1]; /* really bigger, must last */
+ char before_colon[1]; /* really bigger, must be last */
} Htaccess;
-typedef struct
-{
-#ifdef CONFIG_FEATURE_HTTPD_CGI
- char *envp[ENVSIZE+1];
- int envCount;
+/* Must have "next" as a first member */
+typedef struct Htaccess_IP {
+ struct Htaccess_IP *next;
+ unsigned ip;
+ unsigned mask;
+ int allow_deny;
+} Htaccess_IP;
+
+/* Must have "next" as a first member */
+typedef struct Htaccess_Proxy {
+ struct Htaccess_Proxy *next;
+ char *url_from;
+ char *host_port;
+ char *url_to;
+} Htaccess_Proxy;
+
+enum {
+ HTTP_OK = 200,
+ HTTP_PARTIAL_CONTENT = 206,
+ HTTP_MOVED_TEMPORARILY = 302,
+ HTTP_BAD_REQUEST = 400, /* malformed syntax */
+ HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+ HTTP_NOT_FOUND = 404,
+ HTTP_FORBIDDEN = 403,
+ HTTP_REQUEST_TIMEOUT = 408,
+ HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
+ HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_CONTINUE = 100,
+#if 0 /* future use */
+ HTTP_SWITCHING_PROTOCOLS = 101,
+ HTTP_CREATED = 201,
+ HTTP_ACCEPTED = 202,
+ HTTP_NON_AUTHORITATIVE_INFO = 203,
+ HTTP_NO_CONTENT = 204,
+ HTTP_MULTIPLE_CHOICES = 300,
+ HTTP_MOVED_PERMANENTLY = 301,
+ HTTP_NOT_MODIFIED = 304,
+ HTTP_PAYMENT_REQUIRED = 402,
+ HTTP_BAD_GATEWAY = 502,
+ HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
+ HTTP_RESPONSE_SETSIZE = 0xffffffff
#endif
- char buf[MAX_MEMORY_BUFF];
+};
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- const char *realm;
+static const uint16_t http_response_type[] ALIGN2 = {
+ HTTP_OK,
+#if ENABLE_FEATURE_HTTPD_RANGES
+ HTTP_PARTIAL_CONTENT,
+#endif
+ HTTP_MOVED_TEMPORARILY,
+ HTTP_REQUEST_TIMEOUT,
+ HTTP_NOT_IMPLEMENTED,
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ HTTP_UNAUTHORIZED,
+#endif
+ HTTP_NOT_FOUND,
+ HTTP_BAD_REQUEST,
+ HTTP_FORBIDDEN,
+ HTTP_INTERNAL_SERVER_ERROR,
+#if 0 /* not implemented */
+ HTTP_CREATED,
+ HTTP_ACCEPTED,
+ HTTP_NO_CONTENT,
+ HTTP_MULTIPLE_CHOICES,
+ HTTP_MOVED_PERMANENTLY,
+ HTTP_NOT_MODIFIED,
+ HTTP_BAD_GATEWAY,
+ HTTP_SERVICE_UNAVAILABLE,
#endif
- const char *configFile;
+};
- char rmt_ip[16]; /* for set env REMOTE_ADDR */
- unsigned port; /* server initial port and for
- set env REMOTE_PORT */
+static const struct {
+ const char *name;
+ const char *info;
+} http_response[ARRAY_SIZE(http_response_type)] = {
+ { "OK", NULL },
+#if ENABLE_FEATURE_HTTPD_RANGES
+ { "Partial Content", NULL },
+#endif
+ { "Found", NULL },
+ { "Request Timeout", "No request appeared within 60 seconds" },
+ { "Not Implemented", "The requested method is not recognized" },
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ { "Unauthorized", "" },
+#endif
+ { "Not Found", "The requested URL was not found" },
+ { "Bad Request", "Unsupported method" },
+ { "Forbidden", "" },
+ { "Internal Server Error", "Internal Server Error" },
+#if 0 /* not implemented */
+ { "Created" },
+ { "Accepted" },
+ { "No Content" },
+ { "Multiple Choices" },
+ { "Moved Permanently" },
+ { "Not Modified" },
+ { "Bad Gateway", "" },
+ { "Service Unavailable", "" },
+#endif
+};
- const char *found_mime_type;
- off_t ContentLength; /* -1 - unknown */
- time_t last_mod;
- Htaccess *ip_a_d; /* config allow/deny lines */
- int flg_deny_all;
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- Htaccess *auth; /* config user:password lines */
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- Htaccess *mime_a; /* config mime types */
-#endif
+struct globals {
+ int verbose; /* must be int (used by getopt32) */
+ smallint flg_deny_all;
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- int accepted_socket;
-#define a_c_r config->accepted_socket
-#define a_c_w config->accepted_socket
- int debugHttpd; /* if seted, don`t stay daemon */
-#else
-#define a_c_r 0
-#define a_c_w 1
-#endif
-} HttpdConfig;
-
-static HttpdConfig *config;
-
-static const char request_GET[] = "GET"; /* size algorithic optimize */
-
-static const char* const suffixTable [] = {
-/* Warning: shorted equalent suffix in one line must be first */
- ".htm.html", "text/html",
- ".jpg.jpeg", "image/jpeg",
- ".gif", "image/gif",
- ".png", "image/png",
- ".txt.h.c.cc.cpp", "text/plain",
- ".css", "text/css",
- ".wav", "audio/wav",
- ".avi", "video/x-msvideo",
- ".qt.mov", "video/quicktime",
- ".mpe.mpeg", "video/mpeg",
- ".mid.midi", "audio/midi",
- ".mp3", "audio/mpeg",
-#if 0 /* unpopular */
- ".au", "audio/basic",
- ".pac", "application/x-ns-proxy-autoconfig",
- ".vrml.wrl", "model/vrml",
+ unsigned rmt_ip; /* used for IP-based allow/deny rules */
+ time_t last_mod;
+ char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */
+ const char *bind_addr_or_port;
+
+ const char *g_query;
+ const char *opt_c_configFile;
+ const char *home_httpd;
+ const char *index_page;
+
+ const char *found_mime_type;
+ const char *found_moved_temporarily;
+ Htaccess_IP *ip_a_d; /* config allow/deny lines */
+
+ USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
+ USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
+ USE_FEATURE_HTTPD_CGI(char *referer;)
+ USE_FEATURE_HTTPD_CGI(char *user_agent;)
+ USE_FEATURE_HTTPD_CGI(char *host;)
+ USE_FEATURE_HTTPD_CGI(char *http_accept;)
+ USE_FEATURE_HTTPD_CGI(char *http_accept_language;)
+
+ off_t file_size; /* -1 - unknown */
+#if ENABLE_FEATURE_HTTPD_RANGES
+ off_t range_start;
+ off_t range_end;
+ off_t range_len;
#endif
- 0, "application/octet-stream" /* default */
- };
-typedef enum
-{
- HTTP_OK = 200,
- HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
- HTTP_NOT_FOUND = 404,
- HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
- HTTP_BAD_REQUEST = 400, /* malformed syntax */
- HTTP_FORBIDDEN = 403,
- HTTP_INTERNAL_SERVER_ERROR = 500,
-#if 0 /* future use */
- HTTP_CONTINUE = 100,
- HTTP_SWITCHING_PROTOCOLS = 101,
- HTTP_CREATED = 201,
- HTTP_ACCEPTED = 202,
- HTTP_NON_AUTHORITATIVE_INFO = 203,
- HTTP_NO_CONTENT = 204,
- HTTP_MULTIPLE_CHOICES = 300,
- HTTP_MOVED_PERMANENTLY = 301,
- HTTP_MOVED_TEMPORARILY = 302,
- HTTP_NOT_MODIFIED = 304,
- HTTP_PAYMENT_REQUIRED = 402,
- HTTP_BAD_GATEWAY = 502,
- HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
- HTTP_RESPONSE_SETSIZE=0xffffffff
-#endif
-} HttpResponseNum;
-
-typedef struct
-{
- HttpResponseNum type;
- const char *name;
- const char *info;
-} HttpEnumString;
-
-static const HttpEnumString httpResponseNames[] = {
- { HTTP_OK, "OK" },
- { HTTP_NOT_IMPLEMENTED, "Not Implemented",
- "The requested method is not recognized by this server." },
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- { HTTP_UNAUTHORIZED, "Unauthorized", "" },
-#endif
- { HTTP_NOT_FOUND, "Not Found",
- "The requested URL was not found on this server." },
- { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
- { HTTP_FORBIDDEN, "Forbidden", "" },
- { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
- "Internal Server Error" },
-#if 0 /* not implemented */
- { HTTP_CREATED, "Created" },
- { HTTP_ACCEPTED, "Accepted" },
- { HTTP_NO_CONTENT, "No Content" },
- { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
- { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
- { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" },
- { HTTP_NOT_MODIFIED, "Not Modified" },
- { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
- { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *g_auth; /* config user:password lines */
+#endif
+ Htaccess *mime_a; /* config mime types */
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ Htaccess *script_i; /* config script interpreters */
+#endif
+ char *iobuf; /* [IOBUF_SIZE] */
+#define hdr_buf bb_common_bufsiz1
+ char *hdr_ptr;
+ int hdr_cnt;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ const char *http_error_page[ARRAY_SIZE(http_response_type)];
+#endif
+#if ENABLE_FEATURE_HTTPD_PROXY
+ Htaccess_Proxy *proxy;
+#endif
+};
+#define G (*ptr_to_globals)
+#define verbose (G.verbose )
+#define flg_deny_all (G.flg_deny_all )
+#define rmt_ip (G.rmt_ip )
+#define bind_addr_or_port (G.bind_addr_or_port)
+#define g_query (G.g_query )
+#define opt_c_configFile (G.opt_c_configFile )
+#define home_httpd (G.home_httpd )
+#define index_page (G.index_page )
+#define found_mime_type (G.found_mime_type )
+#define found_moved_temporarily (G.found_moved_temporarily)
+#define last_mod (G.last_mod )
+#define ip_a_d (G.ip_a_d )
+#define g_realm (G.g_realm )
+#define remoteuser (G.remoteuser )
+#define referer (G.referer )
+#define user_agent (G.user_agent )
+#define host (G.host )
+#define http_accept (G.http_accept )
+#define http_accept_language (G.http_accept_language)
+#define file_size (G.file_size )
+#if ENABLE_FEATURE_HTTPD_RANGES
+#define range_start (G.range_start )
+#define range_end (G.range_end )
+#define range_len (G.range_len )
+#else
+enum {
+ range_start = 0,
+ range_end = MAXINT(off_t) - 1,
+ range_len = MAXINT(off_t),
+};
#endif
+#define rmt_ip_str (G.rmt_ip_str )
+#define g_auth (G.g_auth )
+#define mime_a (G.mime_a )
+#define script_i (G.script_i )
+#define iobuf (G.iobuf )
+#define hdr_ptr (G.hdr_ptr )
+#define hdr_cnt (G.hdr_cnt )
+#define http_error_page (G.http_error_page )
+#define proxy (G.proxy )
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+ USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
+ bind_addr_or_port = "80"; \
+ index_page = "index.html"; \
+ file_size = -1; \
+} while (0)
+
+
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+
+/* Prototypes */
+enum {
+ SEND_HEADERS = (1 << 0),
+ SEND_BODY = (1 << 1),
+ SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY,
};
+static void send_file_and_exit(const char *url, int what) NORETURN;
+static void free_llist(has_next_ptr **pptr)
+{
+ has_next_ptr *cur = *pptr;
+ while (cur) {
+ has_next_ptr *t = cur;
+ cur = cur->next;
+ free(t);
+ }
+ *pptr = NULL;
+}
-static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
-static const char Content_length[] = "Content-length:";
+static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
+{
+ free_llist((has_next_ptr**)pptr);
+}
+static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
+{
+ free_llist((has_next_ptr**)pptr);
+}
+/* Returns presumed mask width in bits or < 0 on error.
+ * Updates strp, stores IP at provided pointer */
+static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
+{
+ const char *p = *strp;
+ int auto_mask = 8;
+ unsigned ip = 0;
+ int j;
+
+ if (*p == '/')
+ return -auto_mask;
+
+ for (j = 0; j < 4; j++) {
+ unsigned octet;
+
+ if ((*p < '0' || *p > '9') && *p != '/' && *p)
+ return -auto_mask;
+ octet = 0;
+ while (*p >= '0' && *p <= '9') {
+ octet *= 10;
+ octet += *p - '0';
+ if (octet > 255)
+ return -auto_mask;
+ p++;
+ }
+ if (*p == '.')
+ p++;
+ if (*p != '/' && *p)
+ auto_mask += 8;
+ ip = (ip << 8) | octet;
+ }
+ if (*p) {
+ if (*p != endc)
+ return -auto_mask;
+ p++;
+ if (*p == '\0')
+ return -auto_mask;
+ }
+ *ipp = ip;
+ *strp = p;
+ return auto_mask;
+}
-static void free_config_lines(Htaccess **pprev)
+/* Returns 0 on success. Stores IP and mask at provided pointers */
+static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
{
- Htaccess *prev = *pprev;
+ int i;
+ unsigned mask;
+ char *p;
+
+ i = scan_ip(&str, ipp, '/');
+ if (i < 0)
+ return i;
+
+ if (*str) {
+ /* there is /xxx after dotted-IP address */
+ i = bb_strtou(str, &p, 10);
+ if (*p == '.') {
+ /* 'xxx' itself is dotted-IP mask, parse it */
+ /* (return 0 (success) only if it has N.N.N.N form) */
+ return scan_ip(&str, maskp, '\0') - 32;
+ }
+ if (*p)
+ return -1;
+ }
- while( prev ) {
- Htaccess *cur = prev;
+ if (i > 32)
+ return -1;
- prev = cur->next;
- free(cur);
- }
- *pprev = NULL;
+ if (sizeof(unsigned) == 4 && i == 32) {
+ /* mask >>= 32 below may not work */
+ mask = 0;
+ } else {
+ mask = 0xffffffff;
+ mask >>= i;
+ }
+ /* i == 0 -> *maskp = 0x00000000
+ * i == 1 -> *maskp = 0x80000000
+ * i == 4 -> *maskp = 0xf0000000
+ * i == 31 -> *maskp = 0xfffffffe
+ * i == 32 -> *maskp = 0xffffffff */
+ *maskp = (uint32_t)(~mask);
+ return 0;
}
-/* flag */
-#define FIRST_PARSE 0
-#define SUBDIR_PARSE 1
-#define SIGNALED_PARSE 2
-#define FIND_FROM_HTTPD_ROOT 3
-/****************************************************************************
- *
- > $Function: parse_conf()
- *
- * $Description: parse configuration file into in-memory linked list.
- *
- * The first non-white character is examined to determine if the config line
- * is one of the following:
- * .ext:mime/type # new mime type not compiled into httpd
- * [adAD]:from # ip address allow/deny, * for wildcard
- * /path:user:pass # username/password
+/*
+ * Parse configuration file into in-memory linked list.
*
* Any previous IP rules are discarded.
* If the flag argument is not SUBDIR_PARSE then all /path and mime rules
* are also discarded. That is, previous settings are retained if flag is
* SUBDIR_PARSE.
- *
- * $Parameters:
- * (const char *) path . . null for ip address checks, path for password
- * checks.
- * (int) flag . . . . . . the source of the parse request.
- *
- * $Return: (None)
- *
- ****************************************************************************/
+ * Error pages are only parsed on the main config file.
+ *
+ * path Path where to look for httpd.conf (without filename).
+ * flag Type of the parse request.
+ */
+/* flag param: */
+enum {
+ FIRST_PARSE = 0, /* path will be "/etc" */
+ SIGNALED_PARSE = 1, /* path will be "/etc" */
+ SUBDIR_PARSE = 2, /* path will be derived from URL */
+};
static void parse_conf(const char *path, int flag)
{
- FILE *f;
- Htaccess *cur;
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- Htaccess *prev;
-#endif
-
- const char *cf = config->configFile;
- char buf[160];
- char *p0 = NULL;
- char *c, *p;
-
- /* free previous ip setup if present */
- free_config_lines(&config->ip_a_d);
- config->flg_deny_all = 0;
- /* retain previous auth and mime config only for subdir parse */
- if(flag != SUBDIR_PARSE) {
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- free_config_lines(&config->auth)
-#endif
- ; /* appease compiler warnings if option is not set */
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- free_config_lines(&config->mime_a);
-#endif
- }
-
- if(flag == SUBDIR_PARSE || cf == NULL) {
- cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
- if(cf == NULL) {
- if(flag == FIRST_PARSE)
- bb_error_msg_and_die(bb_msg_memory_exhausted);
- return;
+ /* internally used extra flag state */
+ enum { TRY_CURDIR_PARSE = 3 };
+
+ FILE *f;
+ const char *filename;
+ char buf[160];
+
+ /* discard old rules */
+ free_Htaccess_IP_list(&ip_a_d);
+ flg_deny_all = 0;
+ /* retain previous auth and mime config only for subdir parse */
+ if (flag != SUBDIR_PARSE) {
+ free_Htaccess_list(&mime_a);
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free_Htaccess_list(&g_auth);
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ free_Htaccess_list(&script_i);
+#endif
}
- sprintf((char *)cf, "%s/%s", path, httpd_conf);
- }
- while((f = fopen(cf, "r")) == NULL) {
- if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
- /* config file not found, no changes to config */
- return;
+ filename = opt_c_configFile;
+ if (flag == SUBDIR_PARSE || filename == NULL) {
+ filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2);
+ sprintf((char *)filename, "%s/%s", path, HTTPD_CONF);
}
- if(config->configFile && flag == FIRST_PARSE) /* if -c option given */
- bb_perror_msg_and_die("%s", cf);
- flag = FIND_FROM_HTTPD_ROOT;
- cf = httpd_conf;
- }
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- prev = config->auth;
-#endif
- /* This could stand some work */
- while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) {
- c = NULL;
- for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
- if(!isspace(*p0)) {
- *p++ = *p0;
- if(*p0 == ':' && c == NULL)
- c = p;
+
+ while ((f = fopen_for_read(filename)) == NULL) {
+ if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */
+ /* config file not found, no changes to config */
+ return;
}
- }
- *p = 0;
-
- /* test for empty or strange line */
- if (c == NULL || *c == 0)
- continue;
- p0 = buf;
- if(*p0 == 'd')
- *p0 = 'D';
- if(*c == '*') {
- if(*p0 == 'D') {
- /* memorize deny all */
- config->flg_deny_all++;
- }
- /* skip default other "word:*" config lines */
- continue;
+ if (flag == FIRST_PARSE) {
+ /* -c CONFFILE given, but CONFFILE doesn't exist? */
+ if (opt_c_configFile)
+ bb_simple_perror_msg_and_die(opt_c_configFile);
+ /* else: no -c, thus we looked at /etc/httpd.conf,
+ * and it's not there. try ./httpd.conf: */
+ }
+ flag = TRY_CURDIR_PARSE;
+ filename = HTTPD_CONF;
}
- if(*p0 == 'a')
- *p0 = 'A';
- else if(*p0 != 'D'
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- && *p0 != '/'
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- && *p0 != '.'
-#endif
- )
- continue;
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ /* in "/file:user:pass" lines, we prepend path in subdirs */
+ if (flag != SUBDIR_PARSE)
+ path = "";
+#endif
+ /* The lines can be:
+ *
+ * I:default_index_file
+ * H:http_home
+ * [AD]:IP[/mask] # allow/deny, * for wildcard
+ * Ennn:error.html # error page for status nnn
+ * P:/url:[http://]hostname[:port]/new/path # reverse proxy
+ * .ext:mime/type # mime type
+ * *.php:/path/php # run xxx.php through an interpreter
+ * /file:user:pass # username and password
+ */
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ unsigned strlen_buf;
+ unsigned char ch;
+ char *after_colon;
+
+ { /* remove all whitespace, and # comments */
+ char *p, *p0;
+
+ p0 = buf;
+ /* skip non-whitespace beginning. Often the whole line
+ * is non-whitespace. We want this case to work fast,
+ * without needless copying, therefore we don't merge
+ * this operation into next while loop. */
+ while ((ch = *p0) != '\0' && ch != '\n' && ch != '#'
+ && ch != ' ' && ch != '\t'
+ ) {
+ p0++;
+ }
+ p = p0;
+ /* if we enter this loop, we have some whitespace.
+ * discard it */
+ while (ch != '\0' && ch != '\n' && ch != '#') {
+ if (ch != ' ' && ch != '\t') {
+ *p++ = ch;
+ }
+ ch = *++p0;
+ }
+ *p = '\0';
+ strlen_buf = p - buf;
+ if (strlen_buf == 0)
+ continue; /* empty line */
+ }
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- if(*p0 == '/') {
- /* make full path from httpd root / curent_path / config_line_path */
- cf = flag == SUBDIR_PARSE ? path : "";
- p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
- if(p0 == NULL)
- continue;
- c[-1] = 0;
- sprintf(p0, "/%s%s", cf, buf);
-
- /* another call bb_simplify_path */
- cf = p = p0;
-
- do {
- if (*p == '/') {
- if (*cf == '/') { /* skip duplicate (or initial) slash */
- continue;
- } else if (*cf == '.') {
- if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
- continue;
- } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
- ++cf;
- if (p > p0) {
- while (*--p != '/'); /* omit previous dir */
+ after_colon = strchr(buf, ':');
+ /* strange line? */
+ if (after_colon == NULL || *++after_colon == '\0')
+ goto config_error;
+
+ ch = (buf[0] & ~0x20); /* toupper if it's a letter */
+
+ if (ch == 'I') {
+ index_page = xstrdup(after_colon);
+ continue;
+ }
+
+ /* do not allow jumping around using H in subdir's configs */
+ if (flag == FIRST_PARSE && ch == 'H') {
+ home_httpd = xstrdup(after_colon);
+ xchdir(home_httpd);
+ continue;
+ }
+
+ if (ch == 'A' || ch == 'D') {
+ Htaccess_IP *pip;
+
+ if (*after_colon == '*') {
+ if (ch == 'D') {
+ /* memorize "deny all" */
+ flg_deny_all = 1;
}
+ /* skip assumed "A:*", it is a default anyway */
continue;
- }
}
- }
- *++p = *cf;
- } while (*++cf);
-
- if ((p == p0) || (*p != '/')) { /* not a trailing slash */
- ++p; /* so keep last character */
- }
- *p = 0;
- sprintf(p0, "%s:%s", p0, c);
- }
-#endif
- /* storing current config line */
-
- cur = calloc(1, sizeof(Htaccess) + strlen(p0));
- if(cur) {
- cf = strcpy(cur->before_colon, p0);
- c = strchr(cf, ':');
- *c++ = 0;
- cur->after_colon = c;
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- if(*cf == '/')
- free(p0);
-#endif
- if(*cf == 'A' || *cf == 'D') {
- if(*cf == 'D') {
- /* Deny:form_IP move top */
- cur->next = config->ip_a_d;
- config->ip_a_d = cur;
- } else {
- /* add to bottom A:form_IP config line */
- Htaccess *prev_IP = config->ip_a_d;
-
- if(prev_IP == NULL) {
- config->ip_a_d = cur;
+ /* store "allow/deny IP/mask" line */
+ pip = xzalloc(sizeof(*pip));
+ if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) {
+ /* IP{/mask} syntax error detected, protect all */
+ ch = 'D';
+ pip->mask = 0;
+ }
+ pip->allow_deny = ch;
+ if (ch == 'D') {
+ /* Deny:from_IP - prepend */
+ pip->next = ip_a_d;
+ ip_a_d = pip;
} else {
- while(prev_IP->next)
- prev_IP = prev_IP->next;
- prev_IP->next = cur;
+ /* A:from_IP - append (thus all D's precedes A's) */
+ Htaccess_IP *prev_IP = ip_a_d;
+ if (prev_IP == NULL) {
+ ip_a_d = pip;
+ } else {
+ while (prev_IP->next)
+ prev_IP = prev_IP->next;
+ prev_IP->next = pip;
+ }
}
+ continue;
}
- }
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- else if(*cf == '.') {
- /* config .mime line move top for overwrite previous */
- cur->next = config->mime_a;
- config->mime_a = cur;
- }
-#endif
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- else if(prev == NULL) {
- /* first line */
- config->auth = prev = cur;
- } else {
- /* sort path, if current lenght eq or bigger then move up */
- Htaccess *prev_hti = config->auth;
- int l = strlen(cf);
- Htaccess *hti;
-
- for(hti = prev_hti; hti; hti = hti->next) {
- if(l >= strlen(hti->before_colon)) {
- /* insert before hti */
- cur->next = hti;
- if(prev_hti != hti) {
- prev_hti->next = cur;
- } else {
- /* insert as top */
- config->auth = cur;
+
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ if (flag == FIRST_PARSE && ch == 'E') {
+ unsigned i;
+ int status = atoi(buf + 1); /* error status code */
+
+ if (status < HTTP_CONTINUE) {
+ goto config_error;
}
- break;
- }
- if(prev_hti != hti)
- prev_hti = prev_hti->next;
+ /* then error page; find matching status */
+ for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
+ if (http_response_type[i] == status) {
+ /* We chdir to home_httpd, thus no need to
+ * concat_path_file(home_httpd, after_colon)
+ * here */
+ http_error_page[i] = xstrdup(after_colon);
+ break;
+ }
+ }
+ continue;
}
- if(!hti) { /* not inserted, add to bottom */
- prev->next = cur;
- prev = cur;
+#endif
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+ if (flag == FIRST_PARSE && ch == 'P') {
+ /* P:/url:[http://]hostname[:port]/new/path */
+ char *url_from, *host_port, *url_to;
+ Htaccess_Proxy *proxy_entry;
+
+ url_from = after_colon;
+ host_port = strchr(after_colon, ':');
+ if (host_port == NULL) {
+ goto config_error;
+ }
+ *host_port++ = '\0';
+ if (strncmp(host_port, "http://", 7) == 0)
+ host_port += 7;
+ if (*host_port == '\0') {
+ goto config_error;
+ }
+ url_to = strchr(host_port, '/');
+ if (url_to == NULL) {
+ goto config_error;
+ }
+ *url_to = '\0';
+ proxy_entry = xzalloc(sizeof(*proxy_entry));
+ proxy_entry->url_from = xstrdup(url_from);
+ proxy_entry->host_port = xstrdup(host_port);
+ *url_to = '/';
+ proxy_entry->url_to = xstrdup(url_to);
+ proxy_entry->next = proxy;
+ proxy = proxy_entry;
+ continue;
}
- }
#endif
- }
- }
- fclose(f);
+ /* the rest of directives are non-alphabetic,
+ * must avoid using "toupper'ed" ch */
+ ch = buf[0];
+
+ if (ch == '.' /* ".ext:mime/type" */
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ || (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */
+#endif
+ ) {
+ char *p;
+ Htaccess *cur;
+
+ cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf);
+ strcpy(cur->before_colon, buf);
+ p = cur->before_colon + (after_colon - buf);
+ p[-1] = '\0';
+ cur->after_colon = p;
+ if (ch == '.') {
+ /* .mime line: prepend to mime_a list */
+ cur->next = mime_a;
+ mime_a = cur;
+ }
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ else {
+ /* script interpreter line: prepend to script_i list */
+ cur->next = script_i;
+ script_i = cur;
+ }
+#endif
+ continue;
+ }
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (ch == '/') { /* "/file:user:pass" */
+ char *p;
+ Htaccess *cur;
+ unsigned file_len;
+
+ /* note: path is "" unless we are in SUBDIR parse,
+ * otherwise it does NOT start with "/" */
+ cur = xzalloc(sizeof(*cur) /* includes space for NUL */
+ + 1 + strlen(path)
+ + strlen_buf
+ );
+ /* form "/path/file" */
+ sprintf(cur->before_colon, "/%s%.*s",
+ path,
+ (int) (after_colon - buf - 1), /* includes "/", but not ":" */
+ buf);
+ /* canonicalize it */
+ p = bb_simplify_abs_path_inplace(cur->before_colon);
+ file_len = p - cur->before_colon;
+ /* add "user:pass" after NUL */
+ strcpy(++p, after_colon);
+ cur->after_colon = p;
+
+ /* insert cur into g_auth */
+ /* g_auth is sorted by decreased filename length */
+ {
+ Htaccess *auth, **authp;
+
+ authp = &g_auth;
+ while ((auth = *authp) != NULL) {
+ if (file_len >= strlen(auth->before_colon)) {
+ /* insert cur before auth */
+ cur->next = auth;
+ break;
+ }
+ authp = &auth->next;
+ }
+ *authp = cur;
+ }
+ continue;
+ }
+#endif /* BASIC_AUTH */
+
+ /* the line is not recognized */
+ config_error:
+ bb_error_msg("config error '%s' in '%s'", buf, filename);
+ } /* while (fgets) */
+
+ fclose(f);
}
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-/****************************************************************************
- *
- > $Function: encodeString()
- *
- * $Description: Given a string, html encode special characters.
- * This is used for the -e command line option to provide an easy way
- * for scripts to encode result data without confusing browsers. The
- * returned string pointer is memory allocated by malloc().
- *
- * $Parameters:
- * (const char *) string . . The first string to encode.
- *
- * $Return: (char *) . . . .. . . A pointer to the encoded string.
- *
- * $Errors: Returns a null string ("") if memory is not available.
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+/*
+ * Given a string, html-encode special characters.
+ * This is used for the -e command line option to provide an easy way
+ * for scripts to encode result data without confusing browsers. The
+ * returned string pointer is memory allocated by malloc().
*
- ****************************************************************************/
+ * Returns a pointer to the encoded string (malloced).
+ */
static char *encodeString(const char *string)
{
- /* take the simple route and encode everything */
- /* could possibly scan once to get length. */
- int len = strlen(string);
- char *out = malloc(len*5 +1);
- char *p=out;
- char ch;
-
- if (!out) return "";
- while ((ch = *string++)) {
- // very simple check for what to encode
- if (isalnum(ch)) *p++ = ch;
- else p += sprintf(p, "&#%d", (unsigned char) ch);
- }
- *p=0;
- return out;
+ /* take the simple route and encode everything */
+ /* could possibly scan once to get length. */
+ int len = strlen(string);
+ char *out = xmalloc(len * 6 + 1);
+ char *p = out;
+ char ch;
+
+ while ((ch = *string++)) {
+ /* very simple check for what to encode */
+ if (isalnum(ch))
+ *p++ = ch;
+ else
+ p += sprintf(p, "&#%d;", (unsigned char) ch);
+ }
+ *p = '\0';
+ return out;
}
-#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
+#endif /* FEATURE_HTTPD_ENCODE_URL_STR */
-/****************************************************************************
- *
- > $Function: decodeString()
- *
- * $Description: Given a URL encoded string, convert it to plain ascii.
- * Since decoding always makes strings smaller, the decode is done in-place.
- * Thus, callers should strdup() the argument if they do not want the
- * argument modified. The return is the original pointer, allowing this
- * function to be easily used as arguments to other functions.
- *
- * $Parameters:
- * (char *) string . . . The first string to decode.
- * (int) flag . . . 1 if require decode '+' as ' ' for CGI
- *
- * $Return: (char *) . . . . A pointer to the decoded string (same as input).
- *
- * $Errors: None
- *
- ****************************************************************************/
-static char *decodeString(char *orig, int flag_plus_to_space)
+/*
+ * Given a URL encoded string, convert it to plain ascii.
+ * Since decoding always makes strings smaller, the decode is done in-place.
+ * Thus, callers should xstrdup() the argument if they do not want the
+ * argument modified. The return is the original pointer, allowing this
+ * function to be easily used as arguments to other functions.
+ *
+ * string The first string to decode.
+ * option_d 1 if called for httpd -d
+ *
+ * Returns a pointer to the decoded string (same as input).
+ */
+static unsigned hex_to_bin(unsigned char c)
{
- /* note that decoded string is always shorter than original */
- char *string = orig;
- char *ptr = string;
-
- while (*ptr)
- {
- if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
- else if (*ptr != '%') *string++ = *ptr++;
- else {
- unsigned int value;
- sscanf(ptr+1, "%2X", &value);
- *string++ = value;
- ptr += 3;
- }
- }
- *string = '\0';
- return orig;
+ unsigned v;
+
+ v = c - '0';
+ if (v <= 9)
+ return v;
+ /* c | 0x20: letters to lower case, non-letters
+ * to (potentially different) non-letters */
+ v = (unsigned)(c | 0x20) - 'a';
+ if (v <= 5)
+ return v + 10;
+ return ~0;
}
+/* For testing:
+void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
+int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
+t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
+*/
+static char *decodeString(char *orig, int option_d)
+{
+ /* note that decoded string is always shorter than original */
+ char *string = orig;
+ char *ptr = string;
+ char c;
+ while ((c = *ptr++) != '\0') {
+ unsigned v;
-#ifdef CONFIG_FEATURE_HTTPD_CGI
-/****************************************************************************
- *
- > $Function: addEnv()
- *
- * $Description: Add an enviornment variable setting to the global list.
- * A NAME=VALUE string is allocated, filled, and added to the list of
- * environment settings passed to the cgi execution script.
- *
- * $Parameters:
- * (char *) name_before_underline - The first part environment variable name.
- * (char *) name_after_underline - The second part environment variable name.
- * (char *) value . . The value to which the env variable is set.
- *
- * $Return: (void)
- *
- * $Errors: Silently returns if the env runs out of space to hold the new item
- *
- ****************************************************************************/
-static void addEnv(const char *name_before_underline,
- const char *name_after_underline, const char *value)
-{
- char *s;
- const char *underline;
-
- if (config->envCount >= ENVSIZE)
- return;
- if (!value)
- value = "";
- underline = *name_after_underline ? "_" : "";
- asprintf(&s, "%s%s%s=%s", name_before_underline, underline,
- name_after_underline, value);
- if(s) {
- config->envp[config->envCount++] = s;
- config->envp[config->envCount] = 0;
- }
+ if (option_d && c == '+') {
+ *string++ = ' ';
+ continue;
+ }
+ if (c != '%') {
+ *string++ = c;
+ continue;
+ }
+ v = hex_to_bin(ptr[0]);
+ if (v > 15) {
+ bad_hex:
+ if (!option_d)
+ return NULL;
+ *string++ = '%';
+ continue;
+ }
+ v = (v * 16) | hex_to_bin(ptr[1]);
+ if (v > 255)
+ goto bad_hex;
+ if (!option_d && (v == '/' || v == '\0')) {
+ /* caller takes it as indication of invalid
+ * (dangerous wrt exploits) chars */
+ return orig + 1;
+ }
+ *string++ = v;
+ ptr += 2;
+ }
+ *string = '\0';
+ return orig;
}
-#if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || !defined(CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY)
-/* set environs SERVER_PORT and REMOTE_PORT */
-static void addEnvPort(const char *port_name)
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+/*
+ * Decode a base64 data stream as per rfc1521.
+ * Note that the rfc states that non base64 chars are to be ignored.
+ * Since the decode always results in a shorter size than the input,
+ * it is OK to pass the input arg as an output arg.
+ * Parameter: a pointer to a base64 encoded string.
+ * Decoded data is stored in-place.
+ */
+static void decodeBase64(char *Data)
{
- char buf[16];
-
- sprintf(buf, "%u", config->port);
- addEnv(port_name, "PORT", buf);
+ const unsigned char *in = (const unsigned char *)Data;
+ /* The decoded size will be at most 3/4 the size of the encoded */
+ unsigned ch = 0;
+ int i = 0;
+
+ while (*in) {
+ int t = *in++;
+
+ if (t >= '0' && t <= '9')
+ t = t - '0' + 52;
+ else if (t >= 'A' && t <= 'Z')
+ t = t - 'A';
+ else if (t >= 'a' && t <= 'z')
+ t = t - 'a' + 26;
+ else if (t == '+')
+ t = 62;
+ else if (t == '/')
+ t = 63;
+ else if (t == '=')
+ t = 0;
+ else
+ continue;
+
+ ch = (ch << 6) | t;
+ i++;
+ if (i == 4) {
+ *Data++ = (char) (ch >> 16);
+ *Data++ = (char) (ch >> 8);
+ *Data++ = (char) ch;
+ i = 0;
+ }
+ }
+ *Data = '\0';
}
#endif
-#endif /* CONFIG_FEATURE_HTTPD_CGI */
-#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
-/****************************************************************************
- *
- > $Function: addEnvCgi
- *
- * $Description: Create environment variables given a URL encoded arg list.
- * For each variable setting the URL encoded arg list, create a corresponding
- * environment variable. URL encoded arguments have the form
- * name1=value1&name2=value2&name3=&ignores
- * from this example, name3 set empty value, tail without '=' skiping
- *
- * $Parameters:
- * (char *) pargs . . . . A pointer to the URL encoded arguments.
- *
- * $Return: None
- *
- * $Errors: None
- *
- ****************************************************************************/
-static void addEnvCgi(const char *pargs)
+/*
+ * Create a listen server socket on the designated port.
+ */
+static int openServer(void)
{
- char *args;
- char *memargs;
- char *namelist; /* space separated list of arg names */
- if (pargs==0) return;
-
- /* args are a list of name=value&name2=value2 sequences */
- namelist = (char *) malloc(strlen(pargs));
- if (namelist) namelist[0]=0;
- memargs = args = strdup(pargs);
- while (args && *args) {
- const char *name = args;
- char *value = strchr(args, '=');
-
- if (!value) /* &XXX without '=' */
- break;
- *value++ = 0;
- args = strchr(value, '&');
- if (args)
- *args++ = 0;
- addEnv("CGI", name, decodeString(value, 1));
- if (*namelist) strcat(namelist, " ");
- strcat(namelist, name);
- }
- free(memargs);
- if (namelist) {
- addEnv("CGI", "ARGLIST_", namelist);
- free(namelist);
- }
+ unsigned n = bb_strtou(bind_addr_or_port, NULL, 10);
+ if (!errno && n && n <= 0xffff)
+ n = create_and_bind_stream_or_die(NULL, n);
+ else
+ n = create_and_bind_stream_or_die(bind_addr_or_port, 80);
+ xlisten(n, 9);
+ return n;
}
-#endif /* CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV */
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-/****************************************************************************
- *
- > $Function: decodeBase64()
- *
- > $Description: Decode a base 64 data stream as per rfc1521.
- * Note that the rfc states that none base64 chars are to be ignored.
- * Since the decode always results in a shorter size than the input, it is
- * OK to pass the input arg as an output arg.
- *
- * $Parameter:
- * (char *) Data . . . . A pointer to a base64 encoded string.
- * Where to place the decoded data.
- *
- * $Return: void
- *
- * $Errors: None
- *
- ****************************************************************************/
-static void decodeBase64(char *Data)
+/*
+ * Log the connection closure and exit.
+ */
+static void log_and_exit(void) NORETURN;
+static void log_and_exit(void)
{
+ /* Paranoia. IE said to be buggy. It may send some extra data
+ * or be confused by us just exiting without SHUT_WR. Oh well. */
+ shutdown(1, SHUT_WR);
+ /* Why??
+ (this also messes up stdin when user runs httpd -i from terminal)
+ ndelay_on(0);
+ while (read(STDIN_FILENO, iobuf, IOBUF_SIZE) > 0)
+ continue;
+ */
- const unsigned char *in = Data;
- // The decoded size will be at most 3/4 the size of the encoded
- unsigned long ch = 0;
- int i = 0;
-
- while (*in) {
- int t = *in++;
-
- if(t >= '0' && t <= '9')
- t = t - '0' + 52;
- else if(t >= 'A' && t <= 'Z')
- t = t - 'A';
- else if(t >= 'a' && t <= 'z')
- t = t - 'a' + 26;
- else if(t == '+')
- t = 62;
- else if(t == '/')
- t = 63;
- else if(t == '=')
- t = 0;
- else
- continue;
-
- ch = (ch << 6) | t;
- i++;
- if (i == 4) {
- *Data++ = (char) (ch >> 16);
- *Data++ = (char) (ch >> 8);
- *Data++ = (char) ch;
- i = 0;
- }
- }
- *Data = 0;
+ if (verbose > 2)
+ bb_error_msg("closed");
+ _exit(xfunc_error_retval);
}
+
+/*
+ * Create and send HTTP response headers.
+ * The arguments are combined and sent as one write operation. Note that
+ * IE will puke big-time if the headers are not sent in one packet and the
+ * second packet is delayed for any reason.
+ * responseNum - the result code to send.
+ */
+static void send_headers(int responseNum)
+{
+ static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
+
+ const char *responseString = "";
+ const char *infoString = NULL;
+ const char *mime_type;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ const char *error_page = NULL;
+#endif
+ unsigned i;
+ time_t timer = time(NULL);
+ char tmp_str[80];
+ int len;
+
+ for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
+ if (http_response_type[i] == responseNum) {
+ responseString = http_response[i].name;
+ infoString = http_response[i].info;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ error_page = http_error_page[i];
+#endif
+ break;
+ }
+ }
+ /* error message is HTML */
+ mime_type = responseNum == HTTP_OK ?
+ found_mime_type : "text/html";
+
+ if (verbose)
+ bb_error_msg("response:%u", responseNum);
+
+ /* emit the current date */
+ strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&timer));
+ len = sprintf(iobuf,
+ "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
+ "Date: %s\r\nConnection: close\r\n",
+ responseNum, responseString, mime_type, tmp_str);
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (responseNum == HTTP_UNAUTHORIZED) {
+ len += sprintf(iobuf + len,
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ g_realm);
+ }
+#endif
+ if (responseNum == HTTP_MOVED_TEMPORARILY) {
+ len += sprintf(iobuf + len, "Location: %s/%s%s\r\n",
+ found_moved_temporarily,
+ (g_query ? "?" : ""),
+ (g_query ? g_query : ""));
+ }
+
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ if (error_page && access(error_page, R_OK) == 0) {
+ strcat(iobuf, "\r\n");
+ len += 2;
+
+ if (DEBUG)
+ fprintf(stderr, "headers: '%s'\n", iobuf);
+ full_write(STDOUT_FILENO, iobuf, len);
+ if (DEBUG)
+ fprintf(stderr, "writing error page: '%s'\n", error_page);
+ return send_file_and_exit(error_page, SEND_BODY);
+ }
#endif
+ if (file_size != -1) { /* file */
+ strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
+#if ENABLE_FEATURE_HTTPD_RANGES
+ if (responseNum == HTTP_PARTIAL_CONTENT) {
+ len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"d-%"OFF_FMT"d/%"OFF_FMT"d\r\n",
+ range_start,
+ range_end,
+ file_size);
+ file_size = range_end - range_start + 1;
+ }
+#endif
+ len += sprintf(iobuf + len,
+#if ENABLE_FEATURE_HTTPD_RANGES
+ "Accept-Ranges: bytes\r\n"
+#endif
+ "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
+ tmp_str,
+ "Content-length:",
+ file_size
+ );
+ }
+ iobuf[len++] = '\r';
+ iobuf[len++] = '\n';
+ if (infoString) {
+ len += sprintf(iobuf + len,
+ "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n"
+ "<BODY><H1>%d %s</H1>\n%s\n</BODY></HTML>\n",
+ responseNum, responseString,
+ responseNum, responseString, infoString);
+ }
+ if (DEBUG)
+ fprintf(stderr, "headers: '%s'\n", iobuf);
+ if (full_write(STDOUT_FILENO, iobuf, len) != len) {
+ if (verbose > 1)
+ bb_perror_msg("error");
+ log_and_exit();
+ }
+}
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-/****************************************************************************
- *
- > $Function: openServer()
- *
- * $Description: create a listen server socket on the designated port.
- *
- * $Return: (int) . . . A connection socket. -1 for errors.
- *
- * $Errors: None
- *
- ****************************************************************************/
-static int openServer(void)
+static void send_headers_and_exit(int responseNum) NORETURN;
+static void send_headers_and_exit(int responseNum)
{
- struct sockaddr_in lsocket;
- int fd;
-
- /* create the socket right now */
- /* inet_addr() returns a value that is already in network order */
- memset(&lsocket, 0, sizeof(lsocket));
- lsocket.sin_family = AF_INET;
- lsocket.sin_addr.s_addr = INADDR_ANY;
- lsocket.sin_port = htons(config->port) ;
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd >= 0) {
- /* tell the OS it's OK to reuse a previous address even though */
- /* it may still be in a close down state. Allows bind to succeed. */
- int on = 1;
-#ifdef SO_REUSEPORT
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
-#else
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
-#endif
- if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) {
- listen(fd, 9);
- signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
- } else {
- bb_perror_msg_and_die("bind");
- }
- } else {
- bb_perror_msg_and_die("create socket");
- }
- return fd;
+ send_headers(responseNum);
+ log_and_exit();
}
-#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
-/****************************************************************************
- *
- > $Function: sendHeaders()
- *
- * $Description: Create and send HTTP response headers.
- * The arguments are combined and sent as one write operation. Note that
- * IE will puke big-time if the headers are not sent in one packet and the
- * second packet is delayed for any reason.
- *
- * $Parameter:
- * (HttpResponseNum) responseNum . . . The result code to send.
- *
- * $Return: (int) . . . . writing errors
- *
- ****************************************************************************/
-static int sendHeaders(HttpResponseNum responseNum)
+/*
+ * Read from the socket until '\n' or EOF. '\r' chars are removed.
+ * '\n' is replaced with NUL.
+ * Return number of characters read or 0 if nothing is read
+ * ('\r' and '\n' are not counted).
+ * Data is returned in iobuf.
+ */
+static int get_line(void)
{
- char *buf = config->buf;
- const char *responseString = "";
- const char *infoString = 0;
- unsigned int i;
- time_t timer = time(0);
- char timeStr[80];
- int len;
-
- for (i = 0;
- i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) {
- if (httpResponseNames[i].type == responseNum) {
- responseString = httpResponseNames[i].name;
- infoString = httpResponseNames[i].info;
+ int count = 0;
+ char c;
+
+ alarm(HEADER_READ_TIMEOUT);
+ while (1) {
+ if (hdr_cnt <= 0) {
+ hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+ if (hdr_cnt <= 0)
+ break;
+ hdr_ptr = hdr_buf;
+ }
+ iobuf[count] = c = *hdr_ptr++;
+ hdr_cnt--;
+
+ if (c == '\r')
+ continue;
+ if (c == '\n') {
+ iobuf[count] = '\0';
break;
}
- }
- if (responseNum != HTTP_OK) {
- config->found_mime_type = "text/html"; // error message is HTML
- }
-
- /* emit the current date */
- strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
- len = sprintf(buf,
- "HTTP/1.0 %d %s\nContent-type: %s\r\n"
- "Date: %s\r\nConnection: close\r\n",
- responseNum, responseString, config->found_mime_type, timeStr);
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- if (responseNum == HTTP_UNAUTHORIZED) {
- len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
- config->realm);
- }
-#endif
- if (config->ContentLength != -1) { /* file */
- strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
- len += sprintf(buf+len, "Last-Modified: %s\r\n%s %ld\r\n",
- timeStr, Content_length, config->ContentLength);
- }
- strcat(buf, "\r\n");
- len += 2;
- if (infoString) {
- len += sprintf(buf+len,
- "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
- "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
- responseNum, responseString,
- responseNum, responseString, infoString);
- }
-#ifdef DEBUG
- if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf);
-#endif
- return bb_full_write(a_c_w, buf, len);
+ if (count < (IOBUF_SIZE - 1)) /* check overflow */
+ count++;
+ }
+ return count;
}
-/****************************************************************************
- *
- > $Function: getLine()
- *
- * $Description: Read from the socket until an end of line char found.
- *
- * Characters are read one at a time until an eol sequence is found.
- *
- * $Parameters:
- * (char *) buf . . Where to place the read result.
- *
- * $Return: (int) . . . . number of characters read. -1 if error.
- *
- ****************************************************************************/
-static int getLine(char *buf)
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
+
+/* gcc 4.2.1 fares better with NOINLINE */
+static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) NORETURN;
+static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len)
{
- int count = 0;
-
- while (read(a_c_r, buf + count, 1) == 1) {
- if (buf[count] == '\r') continue;
- if (buf[count] == '\n') {
- buf[count] = 0;
- return count;
- }
- if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */
- count++;
- }
- if (count) return count;
- else return -1;
-}
+ enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */
+ struct pollfd pfd[3];
+ int out_cnt; /* we buffer a bit of initial CGI output */
+ int count;
-#ifdef CONFIG_FEATURE_HTTPD_CGI
-/****************************************************************************
- *
- > $Function: sendCgi()
- *
- * $Description: Execute a CGI script and send it's stdout back
- *
- * Environment variables are set up and the script is invoked with pipes
- * for stdin/stdout. If a post is being done the script is fed the POST
- * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
- *
- * $Parameters:
- * (const char *) url . . . The requested URL (with leading /).
- * (const char *urlArgs). . Any URL arguments.
- * (const char *body) . . . POST body contents.
- * (int bodyLen) . . . . . Length of the post body.
- * (const char *cookie) . . For set HTTP_COOKIE.
+ /* iobuf is used for CGI -> network data,
+ * hdr_buf is for network -> CGI data (POSTDATA) */
+
+ /* If CGI dies, we still want to correctly finish reading its output
+ * and send it to the peer. So please no SIGPIPEs! */
+ signal(SIGPIPE, SIG_IGN);
+
+ // We inconsistently handle a case when more POSTDATA from network
+ // is coming than we expected. We may give *some part* of that
+ // extra data to CGI.
+
+ //if (hdr_cnt > post_len) {
+ // /* We got more POSTDATA from network than we expected */
+ // hdr_cnt = post_len;
+ //}
+ post_len -= hdr_cnt;
+ /* post_len - number of POST bytes not yet read from network */
+
+ /* NB: breaking out of this loop jumps to log_and_exit() */
+ out_cnt = 0;
+ while (1) {
+ memset(pfd, 0, sizeof(pfd));
+
+ pfd[FROM_CGI].fd = fromCgi_rd;
+ pfd[FROM_CGI].events = POLLIN;
+
+ if (toCgi_wr) {
+ pfd[TO_CGI].fd = toCgi_wr;
+ if (hdr_cnt > 0) {
+ pfd[TO_CGI].events = POLLOUT;
+ } else if (post_len > 0) {
+ pfd[0].events = POLLIN;
+ } else {
+ /* post_len <= 0 && hdr_cnt <= 0:
+ * no more POST data to CGI,
+ * let CGI see EOF on CGI's stdin */
+ close(toCgi_wr);
+ toCgi_wr = 0;
+ }
+ }
- *
- * $Return: (char *) . . . . A pointer to the decoded string (same as input).
- *
- * $Errors: None
- *
- ****************************************************************************/
-static int sendCgi(const char *url,
- const char *request, const char *urlArgs,
- const char *body, int bodyLen, const char *cookie)
-{
- int fromCgi[2]; /* pipe for reading data from CGI */
- int toCgi[2]; /* pipe for sending data to CGI */
-
- static char * argp[] = { 0, 0 };
- int pid = 0;
- int inFd;
- int outFd;
- int firstLine = 1;
-
- do {
- if (pipe(fromCgi) != 0) {
- break;
- }
- if (pipe(toCgi) != 0) {
- break;
- }
-
- pid = fork();
- if (pid < 0) {
- pid = 0;
- break;
- }
-
- if (!pid) {
- /* child process */
- char *script;
- char *purl = strdup( url );
- char realpath_buff[MAXPATHLEN];
-
- if(purl == NULL)
- _exit(242);
-
- inFd = toCgi[0];
- outFd = fromCgi[1];
-
- dup2(inFd, 0); // replace stdin with the pipe
- dup2(outFd, 1); // replace stdout with the pipe
-
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- if (!config->debugHttpd)
-#endif
- dup2(outFd, 2); // replace stderr with the pipe
-
- close(toCgi[0]);
- close(toCgi[1]);
- close(fromCgi[0]);
- close(fromCgi[1]);
-
- /*
- * Find PATH_INFO.
- */
- script = purl;
- while((script = strchr( script + 1, '/' )) != NULL) {
- /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
- struct stat sb;
+ /* Now wait on the set of sockets */
+ count = safe_poll(pfd, 3, -1);
+ if (count <= 0) {
+#if 0
+ if (safe_waitpid(pid, &status, WNOHANG) <= 0) {
+ /* Weird. CGI didn't exit and no fd's
+ * are ready, yet poll returned?! */
+ continue;
+ }
+ if (DEBUG && WIFEXITED(status))
+ bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status));
+ if (DEBUG && WIFSIGNALED(status))
+ bb_error_msg("CGI killed, signal=%d", WTERMSIG(status));
+#endif
+ break;
+ }
- *script = '\0';
- if(is_directory(purl + 1, 1, &sb) == 0) {
- /* not directory, found script.cgi/PATH_INFO */
- *script = '/';
- break;
- }
- *script = '/'; /* is directory, find next '/' */
- }
- addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */
- addEnv("PATH", "", getenv("PATH"));
- addEnv("REQUEST", "METHOD", request);
- if(urlArgs) {
- char *uri = alloca(strlen(purl) + 2 + strlen(urlArgs));
- if(uri)
- sprintf(uri, "%s?%s", purl, urlArgs);
- addEnv("REQUEST", "URI", uri);
- } else {
- addEnv("REQUEST", "URI", purl);
- }
- if(script != NULL)
- *script = '\0'; /* reduce /PATH_INFO */
- /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
- addEnv("SCRIPT_NAME", "", purl);
- addEnv("QUERY_STRING", "", urlArgs);
- addEnv("SERVER", "SOFTWARE", httpdVersion);
- addEnv("SERVER", "PROTOCOL", "HTTP/1.0");
- addEnv("GATEWAY_INTERFACE", "", "CGI/1.1");
-#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
- addEnv("REMOTE", "ADDR", config->rmt_ip);
- addEnvPort("REMOTE");
-#else
- addEnv("REMOTE_ADDR", "", config->rmt_ip);
-#endif
- if(bodyLen) {
- char sbl[32];
-
- sprintf(sbl, "%d", bodyLen);
- addEnv("CONTENT_LENGTH", "", sbl);
- }
- if(cookie)
- addEnv("HTTP_COOKIE", "", cookie);
-
-#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
- if (request != request_GET) {
- addEnvCgi(body);
- } else {
- addEnvCgi(urlArgs);
- }
-#endif
-
- /* set execve argp[0] without path */
- argp[0] = strrchr( purl, '/' ) + 1;
- /* but script argp[0] must have absolute path and chdiring to this */
- if(realpath(purl + 1, realpath_buff) != NULL) {
- script = strrchr(realpath_buff, '/');
- if(script) {
- *script = '\0';
- if(chdir(realpath_buff) == 0) {
- *script = '/';
- // now run the program. If it fails,
- // use _exit() so no destructors
- // get called and make a mess.
- execve(realpath_buff, argp, config->envp);
+ if (pfd[TO_CGI].revents) {
+ /* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */
+ /* Have data from peer and can write to CGI */
+ count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt);
+ /* Doesn't happen, we dont use nonblocking IO here
+ *if (count < 0 && errno == EAGAIN) {
+ * ...
+ *} else */
+ if (count > 0) {
+ hdr_ptr += count;
+ hdr_cnt -= count;
+ } else {
+ /* EOF/broken pipe to CGI, stop piping POST data */
+ hdr_cnt = post_len = 0;
+ }
}
- }
- }
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- config->accepted_socket = 1; /* send to stdout */
-#endif
- sendHeaders(HTTP_NOT_FOUND);
- _exit(242);
- } /* end child */
-
- } while (0);
-
- if (pid) {
- /* parent process */
- int status;
-
- inFd = fromCgi[0];
- outFd = toCgi[1];
- close(fromCgi[1]);
- close(toCgi[0]);
- if (body) bb_full_write(outFd, body, bodyLen);
- close(outFd);
-
- while (1) {
- struct timeval timeout;
- fd_set readSet;
- char buf[160];
- int nfound;
- int count;
-
- FD_ZERO(&readSet);
- FD_SET(inFd, &readSet);
-
- /* Now wait on the set of sockets! */
- timeout.tv_sec = 0;
- timeout.tv_usec = 10000;
- nfound = select(inFd + 1, &readSet, 0, 0, &timeout);
-
- if (nfound <= 0) {
- if (waitpid(pid, &status, WNOHANG) > 0) {
- close(inFd);
-#ifdef DEBUG
- if (config->debugHttpd) {
- if (WIFEXITED(status))
- bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
- if (WIFSIGNALED(status))
- bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
- }
-#endif
- pid = -1;
- break;
- }
- } else {
- int s = a_c_w;
-
- // There is something to read
- count = bb_full_read(inFd, buf, sizeof(buf)-1);
- // If a read returns 0 at this point then some type of error has
- // occurred. Bail now.
- if (count == 0) break;
- if (count > 0) {
- if (firstLine) {
- /* check to see if the user script added headers */
- if (strncmp(buf, "HTTP/1.0 200 OK\n", 4) != 0) {
- bb_full_write(s, "HTTP/1.0 200 OK\n", 16);
- }
- if (strstr(buf, "ontent-") == 0) {
- bb_full_write(s, "Content-type: text/plain\n\n", 26);
- }
- firstLine=0;
- }
- bb_full_write(s, buf, count);
-#ifdef DEBUG
- if (config->debugHttpd)
- fprintf(stderr, "cgi read %d bytes\n", count);
+
+ if (pfd[0].revents) {
+ /* post_len > 0 && hdr_cnt == 0 here */
+ /* We expect data, prev data portion is eaten by CGI
+ * and there *is* data to read from the peer
+ * (POSTDATA) */
+ //count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len;
+ //count = safe_read(STDIN_FILENO, hdr_buf, count);
+ count = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+ if (count > 0) {
+ hdr_cnt = count;
+ hdr_ptr = hdr_buf;
+ post_len -= count;
+ } else {
+ /* no more POST data can be read */
+ post_len = 0;
+ }
+ }
+
+ if (pfd[FROM_CGI].revents) {
+ /* There is something to read from CGI */
+ char *rbuf = iobuf;
+
+ /* Are we still buffering CGI output? */
+ if (out_cnt >= 0) {
+ /* HTTP_200[] has single "\r\n" at the end.
+ * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
+ * CGI scripts MUST send their own header terminated by
+ * empty line, then data. That's why we have only one
+ * <cr><lf> pair here. We will output "200 OK" line
+ * if needed, but CGI still has to provide blank line
+ * between header and body */
+
+ /* Must use safe_read, not full_read, because
+ * CGI may output a few first bytes and then wait
+ * for POSTDATA without closing stdout.
+ * With full_read we may wait here forever. */
+ count = safe_read(fromCgi_rd, rbuf + out_cnt, PIPE_BUF - 8);
+ if (count <= 0) {
+ /* eof (or error) and there was no "HTTP",
+ * so write it, then write received data */
+ if (out_cnt) {
+ full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1);
+ full_write(STDOUT_FILENO, rbuf, out_cnt);
+ }
+ break; /* CGI stdout is closed, exiting */
+ }
+ out_cnt += count;
+ count = 0;
+ /* "Status" header format is: "Status: 302 Redirected\r\n" */
+ if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
+ /* send "HTTP/1.0 " */
+ if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
+ break;
+ rbuf += 8; /* skip "Status: " */
+ count = out_cnt - 8;
+ out_cnt = -1; /* buffering off */
+ } else if (out_cnt >= 4) {
+ /* Did CGI add "HTTP"? */
+ if (memcmp(rbuf, HTTP_200, 4) != 0) {
+ /* there is no "HTTP", do it ourself */
+ if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+ break;
+ }
+ /* Commented out:
+ if (!strstr(rbuf, "ontent-")) {
+ full_write(s, "Content-type: text/plain\r\n\r\n", 28);
+ }
+ * Counter-example of valid CGI without Content-type:
+ * echo -en "HTTP/1.0 302 Found\r\n"
+ * echo -en "Location: http://www.busybox.net\r\n"
+ * echo -en "\r\n"
+ */
+ count = out_cnt;
+ out_cnt = -1; /* buffering off */
+ }
+ } else {
+ count = safe_read(fromCgi_rd, rbuf, PIPE_BUF);
+ if (count <= 0)
+ break; /* eof (or error) */
+ }
+ if (full_write(STDOUT_FILENO, rbuf, count) != count)
+ break;
+ if (DEBUG)
+ fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+ } /* if (pfd[FROM_CGI].revents) */
+ } /* while (1) */
+ log_and_exit();
+}
#endif
- }
- }
- }
- }
- return 0;
+
+#if ENABLE_FEATURE_HTTPD_CGI
+
+static void setenv1(const char *name, const char *value)
+{
+ setenv(name, value ? value : "", 1);
}
-#endif /* CONFIG_FEATURE_HTTPD_CGI */
-/****************************************************************************
- *
- > $Function: sendFile()
- *
- * $Description: Send a file response to an HTTP request
- *
- * $Parameter:
- * (const char *) url . . The URL requested.
- * (char *) buf . . . . . The stack buffer.
- *
- * $Return: (int) . . . . . . Always 0.
- *
- ****************************************************************************/
-static int sendFile(const char *url, char *buf)
+/*
+ * Spawn CGI script, forward CGI's stdin/out <=> network
+ *
+ * Environment variables are set up and the script is invoked with pipes
+ * for stdin/stdout. If a POST is being done the script is fed the POST
+ * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
+ *
+ * Parameters:
+ * const char *url The requested URL (with leading /).
+ * int post_len Length of the POST body.
+ * const char *cookie For set HTTP_COOKIE.
+ * const char *content_type For set CONTENT_TYPE.
+ */
+static void send_cgi_and_exit(
+ const char *url,
+ const char *request,
+ int post_len,
+ const char *cookie,
+ const char *content_type) NORETURN;
+static void send_cgi_and_exit(
+ const char *url,
+ const char *request,
+ int post_len,
+ const char *cookie,
+ const char *content_type)
{
- char * suffix;
- int f;
- const char * const * table;
- const char * try_suffix;
+ struct fd_pair fromCgi; /* CGI -> httpd pipe */
+ struct fd_pair toCgi; /* httpd -> CGI pipe */
+ char *script;
+ int pid;
+
+ /* Make a copy. NB: caller guarantees:
+ * url[0] == '/', url[1] != '/' */
+ url = xstrdup(url);
+
+ /*
+ * We are mucking with environment _first_ and then vfork/exec,
+ * this allows us to use vfork safely. Parent doesn't care about
+ * these environment changes anyway.
+ */
- suffix = strrchr(url, '.');
+ /* Check for [dirs/]script.cgi/PATH_INFO */
+ script = (char*)url;
+ while ((script = strchr(script + 1, '/')) != NULL) {
+ struct stat sb;
- for (table = suffixTable; *table; table += 2)
- if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
- try_suffix += strlen(suffix);
- if(*try_suffix == 0 || *try_suffix == '.')
+ *script = '\0';
+ if (!is_directory(url + 1, 1, &sb)) {
+ /* not directory, found script.cgi/PATH_INFO */
+ *script = '/';
break;
+ }
+ *script = '/'; /* is directory, find next '/' */
}
- /* also, if not found, set default as "application/octet-stream"; */
- config->found_mime_type = *(table+1);
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
- if (suffix) {
- Htaccess * cur;
-
- for (cur = config->mime_a; cur; cur = cur->next) {
- if(strcmp(cur->before_colon, suffix) == 0) {
- config->found_mime_type = cur->after_colon;
- break;
+ setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */
+ setenv1("REQUEST_METHOD", request);
+ if (g_query) {
+ putenv(xasprintf("%s=%s?%s", "REQUEST_URI", url, g_query));
+ } else {
+ setenv1("REQUEST_URI", url);
}
- }
- }
-#endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
+ if (script != NULL)
+ *script = '\0'; /* cut off /PATH_INFO */
-#ifdef DEBUG
- if (config->debugHttpd)
- fprintf(stderr, "Sending file '%s' Content-type: %s\n",
- url, config->found_mime_type);
+ /* SCRIPT_FILENAME is required by PHP in CGI mode */
+ if (home_httpd[0] == '/') {
+ char *fullpath = concat_path_file(home_httpd, url);
+ setenv1("SCRIPT_FILENAME", fullpath);
+ }
+ /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
+ setenv1("SCRIPT_NAME", url);
+ /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
+ * QUERY_STRING: The information which follows the ? in the URL
+ * which referenced this script. This is the query information.
+ * It should not be decoded in any fashion. This variable
+ * should always be set when there is query information,
+ * regardless of command line decoding. */
+ /* (Older versions of bbox seem to do some decoding) */
+ setenv1("QUERY_STRING", g_query);
+ putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
+ putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
+ putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
+ /* Having _separate_ variables for IP and port defeats
+ * the purpose of having socket abstraction. Which "port"
+ * are you using on Unix domain socket?
+ * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
+ * Oh well... */
+ {
+ char *p = rmt_ip_str ? rmt_ip_str : (char*)"";
+ char *cp = strrchr(p, ':');
+ if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
+ cp = NULL;
+ if (cp) *cp = '\0'; /* delete :PORT */
+ setenv1("REMOTE_ADDR", p);
+ if (cp) {
+ *cp = ':';
+#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ setenv1("REMOTE_PORT", cp + 1);
#endif
-
- f = open(url, O_RDONLY);
- if (f >= 0) {
- int count;
-
- sendHeaders(HTTP_OK);
- while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
- bb_full_write(a_c_w, buf, count);
+ }
+ }
+ setenv1("HTTP_USER_AGENT", user_agent);
+ if (http_accept)
+ setenv1("HTTP_ACCEPT", http_accept);
+ if (http_accept_language)
+ setenv1("HTTP_ACCEPT_LANGUAGE", http_accept_language);
+ if (post_len)
+ putenv(xasprintf("CONTENT_LENGTH=%d", post_len));
+ if (cookie)
+ setenv1("HTTP_COOKIE", cookie);
+ if (content_type)
+ setenv1("CONTENT_TYPE", content_type);
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (remoteuser) {
+ setenv1("REMOTE_USER", remoteuser);
+ putenv((char*)"AUTH_TYPE=Basic");
}
- close(f);
- } else {
-#ifdef DEBUG
- if (config->debugHttpd)
- bb_perror_msg("Unable to open '%s'", url);
#endif
- sendHeaders(HTTP_NOT_FOUND);
- }
+ if (referer)
+ setenv1("HTTP_REFERER", referer);
+ setenv1("HTTP_HOST", host); /* set to "" if NULL */
+ /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
+ * just run "env SERVER_NAME=xyz httpd ..." instead */
- return 0;
-}
+ xpiped_pair(fromCgi);
+ xpiped_pair(toCgi);
-/****************************************************************************
- *
- > $Function: checkPerm()
- *
- * $Description: Check the permission file for access.
- *
- * If config file isn't present, everything is allowed.
- * Entries are of the form you can see example from header source
- *
- * $Parameters:
- * (const char *) path . . . . The file path or NULL for ip addresses.
- * (const char *) request . . . User information to validate.
- *
- * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
- *
- ****************************************************************************/
+ pid = vfork();
+ if (pid < 0) {
+ /* TODO: log perror? */
+ log_and_exit();
+ }
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-static int checkPerm(const char *path, const char *request)
-{
- Htaccess * cur;
- const char *p;
- const char *p0;
-
- int ipaddr = path == NULL;
- const char *prev = NULL;
-
- /* This could stand some work */
- for (cur = ipaddr ? config->ip_a_d : config->auth; cur; cur = cur->next) {
- p0 = cur->before_colon;
- if(prev != NULL && strcmp(prev, p0) != 0)
- continue; /* find next identical */
- p = cur->after_colon;
-#ifdef DEBUG
- if (config->debugHttpd)
- fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
- (ipaddr ? (*p ? p : "*") : p0), request);
-#endif
- if(ipaddr) {
- if(strncmp(p, request, strlen(p)) != 0)
- continue;
- return *p0 == 'A'; /* Allow/Deny */
- } else {
- int l = strlen(p0);
+ if (!pid) {
+ /* Child process */
+ char *argv[3];
+
+ xfunc_error_retval = 242;
+
+ /* NB: close _first_, then move fds! */
+ close(toCgi.wr);
+ close(fromCgi.rd);
+ xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */
+ xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */
+ /* User seeing stderr output can be a security problem.
+ * If CGI really wants that, it can always do dup itself. */
+ /* dup2(1, 2); */
+
+ /* Chdiring to script's dir */
+ script = strrchr(url, '/');
+ if (script != url) { /* paranoia */
+ *script = '\0';
+ if (chdir(url + 1) != 0) {
+ bb_perror_msg("chdir %s", url + 1);
+ goto error_execing_cgi;
+ }
+ // not needed: *script = '/';
+ }
+ script++;
- if(strncmp(p0, path, l) == 0 &&
- (l == 1 || path[l] == '/' || path[l] == 0)) {
- /* path match found. Check request */
+ /* set argv[0] to name without path */
+ argv[0] = script;
+ argv[1] = NULL;
- /* for check next /path:user:password */
- prev = p0;
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
{
- char *cipher;
- char *pp;
- char *u = strchr(request, ':');
-
- if(u == NULL) {
- /* bad request, ':' required */
- continue;
- }
- if(strncmp(p, request, u-request) != 0) {
- /* user uncompared */
- continue;
- }
- pp = strchr(p, ':');
- if(pp && pp[1] == '$' && pp[2] == '1' &&
- pp[3] == '$' && pp[4]) {
- pp++;
- cipher = pw_encrypt(u+1, pp);
- if (strcmp(cipher, pp) == 0)
- return 1; /* Ok */
- /* unauthorized */
- continue;
+ char *suffix = strrchr(script, '.');
+
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ /* found interpreter name */
+ argv[0] = cur->after_colon;
+ argv[1] = script;
+ argv[2] = NULL;
+ break;
+ }
+ }
}
}
#endif
- if (strcmp(p, request) == 0)
- return 1; /* Ok */
- /* unauthorized */
- }
- }
- } /* for */
-
- if(ipaddr)
- return !config->flg_deny_all;
- return prev == NULL;
-}
-
-#else /* ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH */
-static int checkPermIP(const char *request)
-{
- Htaccess * cur;
- const char *p;
-
- /* This could stand some work */
- for (cur = config->ip_a_d; cur; cur = cur->next) {
- p = cur->after_colon;
-#ifdef DEBUG
- if (config->debugHttpd)
- fprintf(stderr, "checkPerm: '%s' ? '%s'\n",
- (*p ? p : "*"), request);
-#endif
- if(strncmp(p, request, strlen(p)) == 0)
- return *cur->before_colon == 'A'; /* Allow/Deny */
- }
-
- /* if uncofigured, return 1 - access from all */
- return !config->flg_deny_all;
+ /* restore default signal dispositions for CGI process */
+ bb_signals(0
+ | (1 << SIGCHLD)
+ | (1 << SIGPIPE)
+ | (1 << SIGHUP)
+ , SIG_DFL);
+
+ /* _NOT_ execvp. We do not search PATH. argv[0] is a filename
+ * without any dir components and will only match a file
+ * in the current directory */
+ execv(argv[0], argv);
+ if (verbose)
+ bb_perror_msg("exec %s", argv[0]);
+ error_execing_cgi:
+ /* send to stdout
+ * (we are CGI here, our stdout is pumped to the net) */
+ send_headers_and_exit(HTTP_NOT_FOUND);
+ } /* end child */
+
+ /* Parent process */
+
+ /* Restore variables possibly changed by child */
+ xfunc_error_retval = 0;
+
+ /* Pump data */
+ close(fromCgi.wr);
+ close(toCgi.rd);
+ cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len);
}
-#define checkPerm(null, request) checkPermIP(request)
-#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+#endif /* FEATURE_HTTPD_CGI */
-/****************************************************************************
- *
- > $Function: handleIncoming()
- *
- * $Description: Handle an incoming http request.
+/*
+ * Send a file response to a HTTP request, and exit
*
- ****************************************************************************/
-static void handleIncoming(void)
+ * Parameters:
+ * const char *url The requested URL (with leading /).
+ * what What to send (headers/body/both).
+ */
+static NOINLINE void send_file_and_exit(const char *url, int what)
{
- char *buf = config->buf;
- char *url;
- char *purl;
- int blank = -1;
- char *urlArgs;
-#ifdef CONFIG_FEATURE_HTTPD_CGI
- const char *prequest = request_GET;
- char *body = 0;
- long length=0;
- char *cookie = 0;
-#endif
- char *test;
- struct stat sb;
- int ip_allowed;
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- int credentials = -1; /* if not requred this is Ok */
-#endif
-
- do {
- int count;
-
- if (getLine(buf) <= 0)
- break; /* closed */
-
- purl = strpbrk(buf, " \t");
- if(purl == NULL) {
-BAD_REQUEST:
- sendHeaders(HTTP_BAD_REQUEST);
- break;
- }
- *purl = 0;
-#ifdef CONFIG_FEATURE_HTTPD_CGI
- if(strcasecmp(buf, prequest) != 0) {
- prequest = "POST";
- if(strcasecmp(buf, prequest) != 0) {
- sendHeaders(HTTP_NOT_IMPLEMENTED);
- break;
+ static const char *const suffixTable[] = {
+ /* Warning: shorter equivalent suffix in one line must be first */
+ ".htm.html", "text/html",
+ ".jpg.jpeg", "image/jpeg",
+ ".gif", "image/gif",
+ ".png", "image/png",
+ ".txt.h.c.cc.cpp", "text/plain",
+ ".css", "text/css",
+ ".wav", "audio/wav",
+ ".avi", "video/x-msvideo",
+ ".qt.mov", "video/quicktime",
+ ".mpe.mpeg", "video/mpeg",
+ ".mid.midi", "audio/midi",
+ ".mp3", "audio/mpeg",
+#if 0 /* unpopular */
+ ".au", "audio/basic",
+ ".pac", "application/x-ns-proxy-autoconfig",
+ ".vrml.wrl", "model/vrml",
+#endif
+ NULL
+ };
+
+ char *suffix;
+ int fd;
+ const char *const *table;
+ const char *try_suffix;
+ ssize_t count;
+
+ fd = open(url, O_RDONLY);
+ if (fd < 0) {
+ if (DEBUG)
+ bb_perror_msg("can't open '%s'", url);
+ /* Error pages are sent by using send_file_and_exit(SEND_BODY).
+ * IOW: it is unsafe to call send_headers_and_exit
+ * if what is SEND_BODY! Can recurse! */
+ if (what != SEND_BODY)
+ send_headers_and_exit(HTTP_NOT_FOUND);
+ log_and_exit();
}
- }
-#else
- if(strcasecmp(buf, request_GET) != 0) {
- sendHeaders(HTTP_NOT_IMPLEMENTED);
- break;
- }
-#endif
- *purl = ' ';
- count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
-
- decodeString(buf, 0);
- if (count < 1 || buf[0] != '/') {
- /* Garbled request/URL */
- goto BAD_REQUEST;
- }
- url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */
- if(url == NULL) {
- sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
- break;
- }
- strcpy(url, buf);
- /* extract url args if present */
- urlArgs = strchr(url, '?');
- if (urlArgs)
- *urlArgs++ = 0;
-
- /* algorithm stolen from libbb bb_simplify_path(),
- but don`t strdup and reducing trailing slash and protect out root */
- purl = test = url;
-
- do {
- if (*purl == '/') {
- if (*test == '/') { /* skip duplicate (or initial) slash */
- continue;
- } else if (*test == '.') {
- if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
- continue;
- } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
- ++test;
- if (purl == url) {
- /* protect out root */
- goto BAD_REQUEST;
- }
- while (*--purl != '/'); /* omit previous dir */
- continue;
+ /* If you want to know about EPIPE below
+ * (happens if you abort downloads from local httpd): */
+ signal(SIGPIPE, SIG_IGN);
+
+ suffix = strrchr(url, '.');
+
+ /* If not found, set default as "application/octet-stream"; */
+ found_mime_type = "application/octet-stream";
+ if (suffix) {
+ Htaccess *cur;
+ for (table = suffixTable; *table; table += 2) {
+ try_suffix = strstr(table[0], suffix);
+ if (try_suffix) {
+ try_suffix += strlen(suffix);
+ if (*try_suffix == '\0' || *try_suffix == '.') {
+ found_mime_type = table[1];
+ break;
+ }
+ }
+ }
+ for (cur = mime_a; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon, suffix) == 0) {
+ found_mime_type = cur->after_colon;
+ break;
+ }
}
- }
- }
- *++purl = *test;
- } while (*++test);
-
- *++purl = 0; /* so keep last character */
- test = purl; /* end ptr */
-
- /* If URL is directory, adding '/' */
- if(test[-1] != '/') {
- if ( is_directory(url + 1, 1, &sb) ) {
- *test++ = '/';
- *test = 0;
- purl = test; /* end ptr */
- }
- }
-#ifdef DEBUG
- if (config->debugHttpd)
- fprintf(stderr, "url='%s', args=%s\n", url, urlArgs);
-#endif
-
- test = url;
- ip_allowed = checkPerm(NULL, config->rmt_ip);
- while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
- /* have path1/path2 */
- *test = '\0';
- if( is_directory(url + 1, 1, &sb) ) {
- /* may be having subdir config */
- parse_conf(url + 1, SUBDIR_PARSE);
- ip_allowed = checkPerm(NULL, config->rmt_ip);
}
- *test = '/';
- }
-
- // read until blank line for HTTP version specified, else parse immediate
- while (blank >= 0 && (count = getLine(buf)) > 0) {
-#ifdef DEBUG
- if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf);
-#endif
-
-#ifdef CONFIG_FEATURE_HTTPD_CGI
- /* try and do our best to parse more lines */
- if ((strncasecmp(buf, Content_length, 15) == 0)) {
- if(prequest != request_GET)
- length = strtol(buf + 15, 0, 0); // extra read only for POST
- } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
- for(test = buf + 7; isspace(*test); test++)
- ;
- cookie = strdup(test);
- }
-#endif
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- if (strncasecmp(buf, "Authorization:", 14) == 0) {
- /* We only allow Basic credentials.
- * It shows up as "Authorization: Basic <userid:password>" where
- * the userid:password is base64 encoded.
- */
- for(test = buf + 14; isspace(*test); test++)
- ;
- if (strncasecmp(test, "Basic", 5) != 0)
- continue;
-
- test += 5; /* decodeBase64() skiping space self */
- decodeBase64(test);
- credentials = checkPerm(url, test);
- }
-#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
-
- } /* while extra header reading */
-
-
- if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
- /* protect listing [/path]/httpd_conf or IP deny */
-#ifdef CONFIG_FEATURE_HTTPD_CGI
-FORBIDDEN: /* protect listing /cgi-bin */
-#endif
- sendHeaders(HTTP_FORBIDDEN);
- break;
- }
-
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- if (credentials <= 0 && checkPerm(url, ":") == 0) {
- sendHeaders(HTTP_UNAUTHORIZED);
- break;
- }
-#endif
-
- test = url + 1; /* skip first '/' */
-
-#ifdef CONFIG_FEATURE_HTTPD_CGI
- /* if strange Content-Length */
- if (length < 0 || length > MAX_POST_SIZE)
- break;
-
- if (length > 0) {
- body = malloc(length + 1);
- if (body) {
- length = bb_full_read(a_c_r, body, length);
- if(length < 0) // closed
- length = 0;
- body[length] = 0; // always null terminate for safety
- }
- }
-
- if (strncmp(test, "cgi-bin", 7) == 0) {
- if(test[7] == '/' && test[8] == 0)
- goto FORBIDDEN; // protect listing cgi-bin/
- sendCgi(url, prequest, urlArgs, body, length, cookie);
- } else {
- if (prequest != request_GET)
- sendHeaders(HTTP_NOT_IMPLEMENTED);
- else {
-#endif /* CONFIG_FEATURE_HTTPD_CGI */
- if(purl[-1] == '/')
- strcpy(purl, "index.html");
- if ( stat(test, &sb ) == 0 ) {
- config->ContentLength = sb.st_size;
- config->last_mod = sb.st_mtime;
+ if (DEBUG)
+ bb_error_msg("sending file '%s' content-type: %s",
+ url, found_mime_type);
+
+#if ENABLE_FEATURE_HTTPD_RANGES
+ if (what == SEND_BODY)
+ range_start = 0; /* err pages and ranges don't mix */
+ range_len = MAXINT(off_t);
+ if (range_start) {
+ if (!range_end) {
+ range_end = file_size - 1;
+ }
+ if (range_end < range_start
+ || lseek(fd, range_start, SEEK_SET) != range_start
+ ) {
+ lseek(fd, 0, SEEK_SET);
+ range_start = 0;
+ } else {
+ range_len = range_end - range_start + 1;
+ send_headers(HTTP_PARTIAL_CONTENT);
+ what = SEND_BODY;
}
- sendFile(test, buf);
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- /* unset if non inetd looped */
- config->ContentLength = -1;
+ }
#endif
-
-#ifdef CONFIG_FEATURE_HTTPD_CGI
+ if (what & SEND_HEADERS)
+ send_headers(HTTP_OK);
+#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+ {
+ off_t offset = range_start;
+ while (1) {
+ /* sz is rounded down to 64k */
+ ssize_t sz = MAXINT(ssize_t) - 0xffff;
+ USE_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)
+ count = sendfile(STDOUT_FILENO, fd, &offset, sz);
+ if (count < 0) {
+ if (offset == range_start)
+ break; /* fall back to read/write loop */
+ goto fin;
+ }
+ USE_FEATURE_HTTPD_RANGES(range_len -= sz;)
+ if (count == 0 || range_len == 0)
+ log_and_exit();
+ }
}
- }
#endif
+ while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) {
+ ssize_t n;
+ USE_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)
+ n = full_write(STDOUT_FILENO, iobuf, count);
+ if (count != n)
+ break;
+ USE_FEATURE_HTTPD_RANGES(range_len -= count;)
+ if (range_len == 0)
+ break;
+ }
+ if (count < 0) {
+ USE_FEATURE_HTTPD_USE_SENDFILE(fin:)
+ if (verbose > 1)
+ bb_perror_msg("error");
+ }
+ log_and_exit();
+}
- } while (0);
-
+static int checkPermIP(void)
+{
+ Htaccess_IP *cur;
+
+ for (cur = ip_a_d; cur; cur = cur->next) {
+#if DEBUG
+ fprintf(stderr,
+ "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
+ rmt_ip_str,
+ (unsigned char)(cur->ip >> 24),
+ (unsigned char)(cur->ip >> 16),
+ (unsigned char)(cur->ip >> 8),
+ (unsigned char)(cur->ip),
+ (unsigned char)(cur->mask >> 24),
+ (unsigned char)(cur->mask >> 16),
+ (unsigned char)(cur->mask >> 8),
+ (unsigned char)(cur->mask)
+ );
+#endif
+ if ((rmt_ip & cur->mask) == cur->ip)
+ return (cur->allow_deny == 'A'); /* A -> 1 */
+ }
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-/* from inetd don`t looping: freeing, closing automatic from exit always */
-# ifdef DEBUG
- if (config->debugHttpd) fprintf(stderr, "closing socket\n");
-# endif
-# ifdef CONFIG_FEATURE_HTTPD_CGI
- free(body);
- free(cookie);
-# endif
- shutdown(a_c_w, SHUT_WR);
- shutdown(a_c_r, SHUT_RD);
- close(config->accepted_socket);
-#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+ return !flg_deny_all; /* depends on whether we saw "D:*" */
}
-/****************************************************************************
- *
- > $Function: miniHttpd()
- *
- * $Description: The main http server function.
- *
- * Given an open socket fildes, listen for new connections and farm out
- * the processing as a forked process.
- *
- * $Parameters:
- * (int) server. . . The server socket fildes.
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+/*
+ * Config file entries are of the form "/<path>:<user>:<passwd>".
+ * If config file has no prefix match for path, access is allowed.
*
- * $Return: (int) . . . . Always 0.
+ * path The file path
+ * user_and_passwd "user:passwd" to validate
*
- ****************************************************************************/
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-static int miniHttpd(int server)
+ * Returns 1 if user_and_passwd is OK.
+ */
+static int check_user_passwd(const char *path, const char *user_and_passwd)
{
- fd_set readfd, portfd;
+ Htaccess *cur;
+ const char *prev = NULL;
+
+ for (cur = g_auth; cur; cur = cur->next) {
+ const char *dir_prefix;
+ size_t len;
+
+ dir_prefix = cur->before_colon;
+
+ /* WHY? */
+ /* If already saw a match, don't accept other different matches */
+ if (prev && strcmp(prev, dir_prefix) != 0)
+ continue;
+
+ if (DEBUG)
+ fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
+
+ /* If it's not a prefix match, continue searching */
+ len = strlen(dir_prefix);
+ if (len != 1 /* dir_prefix "/" matches all, don't need to check */
+ && (strncmp(dir_prefix, path, len) != 0
+ || (path[len] != '/' && path[len] != '\0'))
+ ) {
+ continue;
+ }
+
+ /* Path match found */
+ prev = dir_prefix;
- FD_ZERO(&portfd);
- FD_SET(server, &portfd);
+ if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
+ char *md5_passwd;
- /* copy the ports we are watching to the readfd set */
- while (1) {
- readfd = portfd;
+ md5_passwd = strchr(cur->after_colon, ':');
+ if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1'
+ && md5_passwd[3] == '$' && md5_passwd[4]
+ ) {
+ char *encrypted;
+ int r, user_len_p1;
- /* Now wait INDEFINATELY on the set of sockets! */
- if (select(server + 1, &readfd, 0, 0, 0) > 0) {
- if (FD_ISSET(server, &readfd)) {
- int on;
- struct sockaddr_in fromAddr;
+ md5_passwd++;
+ user_len_p1 = md5_passwd - cur->after_colon;
+ /* comparing "user:" */
+ if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) {
+ continue;
+ }
- unsigned int addr;
- socklen_t fromAddrLen = sizeof(fromAddr);
- int s = accept(server,
- (struct sockaddr *)&fromAddr, &fromAddrLen);
+ encrypted = pw_encrypt(
+ user_and_passwd + user_len_p1 /* cleartext pwd from user */,
+ md5_passwd /*salt */, 1 /* cleanup */);
+ r = strcmp(encrypted, md5_passwd);
+ free(encrypted);
+ if (r == 0)
+ goto set_remoteuser_var; /* Ok */
+ continue;
+ }
+ }
- if (s < 0) {
- continue;
- }
- config->accepted_socket = s;
- addr = ntohl(fromAddr.sin_addr.s_addr);
- sprintf(config->rmt_ip, "%u.%u.%u.%u",
- (unsigned char)(addr >> 24),
- (unsigned char)(addr >> 16),
- (unsigned char)(addr >> 8),
- addr & 0xff);
- config->port = ntohs(fromAddr.sin_port);
-#ifdef DEBUG
- if (config->debugHttpd) {
- bb_error_msg("connection from IP=%s, port %u\n",
- config->rmt_ip, config->port);
- }
-#endif
- /* set the KEEPALIVE option to cull dead connections */
- on = 1;
- setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
+ /* Comparing plaintext "user:pass" in one go */
+ if (strcmp(cur->after_colon, user_and_passwd) == 0) {
+ set_remoteuser_var:
+ remoteuser = xstrndup(user_and_passwd,
+ strchrnul(user_and_passwd, ':') - user_and_passwd);
+ return 1; /* Ok */
+ }
+ } /* for */
- if (config->debugHttpd || fork() == 0) {
- /* This is the spawned thread */
-#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
- /* protect reload config, may be confuse checking */
- signal(SIGHUP, SIG_IGN);
-#endif
- handleIncoming();
- if(!config->debugHttpd)
- exit(0);
- }
- close(s);
- }
- }
- } // while (1)
- return 0;
+ /* 0(bad) if prev is set: matches were found but passwd was wrong */
+ return (prev == NULL);
}
+#endif /* FEATURE_HTTPD_BASIC_AUTH */
-#else
- /* from inetd */
-
-static int miniHttpd(void)
+#if ENABLE_FEATURE_HTTPD_PROXY
+static Htaccess_Proxy *find_proxy_entry(const char *url)
{
- struct sockaddr_in fromAddrLen;
- socklen_t sinlen = sizeof (struct sockaddr_in);
- unsigned int addr;
-
- getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
- addr = ntohl(fromAddrLen.sin_addr.s_addr);
- sprintf(config->rmt_ip, "%u.%u.%u.%u",
- (unsigned char)(addr >> 24),
- (unsigned char)(addr >> 16),
- (unsigned char)(addr >> 8),
- addr & 0xff);
- config->port = ntohs(fromAddrLen.sin_port);
- handleIncoming();
- return 0;
+ Htaccess_Proxy *p;
+ for (p = proxy; p; p = p->next) {
+ if (strncmp(url, p->url_from, strlen(p->url_from)) == 0)
+ return p;
+ }
+ return NULL;
}
-#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+#endif
-#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
-static void sighup_handler(int sig)
+/*
+ * Handle timeouts
+ */
+static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
+static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
{
- /* set and reset */
- struct sigaction sa;
-
- sa.sa_handler = sighup_handler;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGHUP, &sa, NULL);
- parse_conf(default_path_httpd_conf,
- sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
+ send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
}
-#endif
+/*
+ * Handle an incoming http request and exit.
+ */
+static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
+static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
+{
+ static const char request_GET[] ALIGN1 = "GET";
+ struct stat sb;
+ char *urlcopy;
+ char *urlp;
+ char *tptr;
+#if ENABLE_FEATURE_HTTPD_CGI
+ static const char request_HEAD[] ALIGN1 = "HEAD";
+ const char *prequest;
+ char *cookie = NULL;
+ char *content_type = NULL;
+ unsigned long length = 0;
+#elif ENABLE_FEATURE_HTTPD_PROXY
+#define prequest request_GET
+ unsigned long length = 0;
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ smallint authorized = -1;
+#endif
+ smallint ip_allowed;
+ char http_major_version;
+#if ENABLE_FEATURE_HTTPD_PROXY
+ char http_minor_version;
+ char *header_buf = header_buf; /* for gcc */
+ char *header_ptr = header_ptr;
+ Htaccess_Proxy *proxy_entry;
+#endif
+
+ /* Allocation of iobuf is postponed until now
+ * (IOW, server process doesn't need to waste 8k) */
+ iobuf = xmalloc(IOBUF_SIZE);
+
+ rmt_ip = 0;
+ if (fromAddr->u.sa.sa_family == AF_INET) {
+ rmt_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr->u.sa.sa_family == AF_INET6
+ && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
+ && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
+ && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
+ rmt_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
+#endif
+ if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
+ /* NB: can be NULL (user runs httpd -i by hand?) */
+ rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
+ }
+ if (verbose) {
+ /* this trick makes -v logging much simpler */
+ if (rmt_ip_str)
+ applet_name = rmt_ip_str;
+ if (verbose > 2)
+ bb_error_msg("connected");
+ }
-static const char httpd_opts[]="c:d:h:"
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
- "e:"
-#define OPT_INC_1 1
+ /* Install timeout handler. get_line() needs it. */
+ signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
+
+ if (!get_line()) /* EOF or error or empty line */
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+
+ /* Determine type of request (GET/POST) */
+ urlp = strpbrk(iobuf, " \t");
+ if (urlp == NULL)
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+ *urlp++ = '\0';
+#if ENABLE_FEATURE_HTTPD_CGI
+ prequest = request_GET;
+ if (strcasecmp(iobuf, prequest) != 0) {
+ prequest = request_HEAD;
+ if (strcasecmp(iobuf, prequest) != 0) {
+ prequest = "POST";
+ if (strcasecmp(iobuf, prequest) != 0)
+ send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
+ }
+ }
#else
-#define OPT_INC_1 0
+ if (strcasecmp(iobuf, request_GET) != 0)
+ send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
+#endif
+ urlp = skip_whitespace(urlp);
+ if (urlp[0] != '/')
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+
+ /* Find end of URL and parse HTTP version, if any */
+ http_major_version = '0';
+ USE_FEATURE_HTTPD_PROXY(http_minor_version = '0';)
+ tptr = strchrnul(urlp, ' ');
+ /* Is it " HTTP/"? */
+ if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) {
+ http_major_version = tptr[6];
+ USE_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];)
+ }
+ *tptr = '\0';
+
+ /* Copy URL from after "GET "/"POST " to stack-allocated char[] */
+ urlcopy = alloca((tptr - urlp) + 2 + strlen(index_page));
+ /*if (urlcopy == NULL)
+ * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/
+ strcpy(urlcopy, urlp);
+ /* NB: urlcopy ptr is never changed after this */
+
+ /* Extract url args if present */
+ g_query = NULL;
+ tptr = strchr(urlcopy, '?');
+ if (tptr) {
+ *tptr++ = '\0';
+ g_query = tptr;
+ }
+
+ /* Decode URL escape sequences */
+ tptr = decodeString(urlcopy, 0);
+ if (tptr == NULL)
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+ if (tptr == urlcopy + 1) {
+ /* '/' or NUL is encoded */
+ send_headers_and_exit(HTTP_NOT_FOUND);
+ }
+
+ /* Canonicalize path */
+ /* Algorithm stolen from libbb bb_simplify_path(),
+ * but don't strdup, retain trailing slash, protect root */
+ urlp = tptr = urlcopy;
+ do {
+ if (*urlp == '/') {
+ /* skip duplicate (or initial) slash */
+ if (*tptr == '/') {
+ continue;
+ }
+ if (*tptr == '.') {
+ /* skip extra "/./" */
+ if (tptr[1] == '/' || !tptr[1]) {
+ continue;
+ }
+ /* "..": be careful */
+ if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) {
+ ++tptr;
+ if (urlp == urlcopy) /* protect root */
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+ while (*--urlp != '/') /* omit previous dir */;
+ continue;
+ }
+ }
+ }
+ *++urlp = *tptr;
+ } while (*++tptr);
+ *++urlp = '\0'; /* terminate after last character */
+
+ /* If URL is a directory, add '/' */
+ if (urlp[-1] != '/') {
+ if (is_directory(urlcopy + 1, 1, &sb)) {
+ found_moved_temporarily = urlcopy;
+ }
+ }
+
+ /* Log it */
+ if (verbose > 1)
+ bb_error_msg("url:%s", urlcopy);
+
+ tptr = urlcopy;
+ ip_allowed = checkPermIP();
+ while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {
+ /* have path1/path2 */
+ *tptr = '\0';
+ if (is_directory(urlcopy + 1, 1, &sb)) {
+ /* may have subdir config */
+ parse_conf(urlcopy + 1, SUBDIR_PARSE);
+ ip_allowed = checkPermIP();
+ }
+ *tptr = '/';
+ }
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+ proxy_entry = find_proxy_entry(urlcopy);
+ if (proxy_entry)
+ header_buf = header_ptr = xmalloc(IOBUF_SIZE);
+#endif
+
+ if (http_major_version >= '0') {
+ /* Request was with "... HTTP/nXXX", and n >= 0 */
+
+ /* Read until blank line for HTTP version specified, else parse immediate */
+ while (1) {
+ if (!get_line())
+ break; /* EOF or error or empty line */
+ if (DEBUG)
+ bb_error_msg("header: '%s'", iobuf);
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+ /* We need 2 more bytes for yet another "\r\n" -
+ * see near fdprintf(proxy_fd...) further below */
+ if (proxy_entry && (header_ptr - header_buf) < IOBUF_SIZE - 2) {
+ int len = strlen(iobuf);
+ if (len > IOBUF_SIZE - (header_ptr - header_buf) - 4)
+ len = IOBUF_SIZE - (header_ptr - header_buf) - 4;
+ memcpy(header_ptr, iobuf, len);
+ header_ptr += len;
+ header_ptr[0] = '\r';
+ header_ptr[1] = '\n';
+ header_ptr += 2;
+ }
#endif
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- "r:"
-# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
- "m:"
-# define OPT_INC_2 2
-# else
-# define OPT_INC_2 1
+
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
+ /* Try and do our best to parse more lines */
+ if ((STRNCASECMP(iobuf, "Content-length:") == 0)) {
+ /* extra read only for POST */
+ if (prequest != request_GET
+#if ENABLE_FEATURE_HTTPD_CGI
+ && prequest != request_HEAD
+#endif
+ ) {
+ tptr = skip_whitespace(iobuf + sizeof("Content-length:") - 1);
+ if (!tptr[0])
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+ /* not using strtoul: it ignores leading minus! */
+ length = bb_strtou(tptr, NULL, 10);
+ /* length is "ulong", but we need to pass it to int later */
+ if (errno || length > INT_MAX)
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+ }
+ }
#endif
-#else
-#define OPT_INC_2 0
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
+ cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
+ } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
+ content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
+ } else if (STRNCASECMP(iobuf, "Referer:") == 0) {
+ referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
+ } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
+ user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
+ } else if (STRNCASECMP(iobuf, "Host:") == 0) {
+ host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
+ } else if (STRNCASECMP(iobuf, "Accept:") == 0) {
+ http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
+ } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
+ http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1));
+ }
#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- "p:v"
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
- "u:"
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (STRNCASECMP(iobuf, "Authorization:") == 0) {
+ /* We only allow Basic credentials.
+ * It shows up as "Authorization: Basic <user>:<passwd>" where
+ * "<user>:<passwd>" is base64 encoded.
+ */
+ tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
+ if (STRNCASECMP(tptr, "Basic") != 0)
+ continue;
+ tptr += sizeof("Basic")-1;
+ /* decodeBase64() skips whitespace itself */
+ decodeBase64(tptr);
+ authorized = check_user_passwd(urlcopy, tptr);
+ }
#endif
-#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
- ;
+#if ENABLE_FEATURE_HTTPD_RANGES
+ if (STRNCASECMP(iobuf, "Range:") == 0) {
+ /* We know only bytes=NNN-[MMM] */
+ char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
+ if (strncmp(s, "bytes=", 6) == 0) {
+ s += sizeof("bytes=")-1;
+ range_start = BB_STRTOOFF(s, &s, 10);
+ if (s[0] != '-' || range_start < 0) {
+ range_start = 0;
+ } else if (s[1]) {
+ range_end = BB_STRTOOFF(s+1, NULL, 10);
+ if (errno || range_end < range_start)
+ range_start = 0;
+ }
+ }
+ }
+#endif
+ } /* while extra header reading */
+ }
-#define OPT_CONFIG_FILE (1<<0)
-#define OPT_DECODE_URL (1<<1)
-#define OPT_HOME_HTTPD (1<<2)
-#define OPT_ENCODE_URL (1<<(2+OPT_INC_1))
-#define OPT_REALM (1<<(3+OPT_INC_1))
-#define OPT_MD5 (1<<(4+OPT_INC_1))
-#define OPT_PORT (1<<(3+OPT_INC_1+OPT_INC_2))
-#define OPT_DEBUG (1<<(4+OPT_INC_1+OPT_INC_2))
-#define OPT_SETUID (1<<(5+OPT_INC_1+OPT_INC_2))
+ /* We are done reading headers, disable peer timeout */
+ alarm(0);
+ if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) {
+ /* protect listing [/path]/httpd.conf or IP deny */
+ send_headers_and_exit(HTTP_FORBIDDEN);
+ }
-#ifdef HTTPD_STANDALONE
-int main(int argc, char *argv[])
-#else
-int httpd_main(int argc, char *argv[])
-#endif
-{
- unsigned long opt;
- const char *home_httpd = home;
- char *url_for_decode;
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
- const char *url_for_encode;
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- const char *s_port;
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ /* Case: no "Authorization:" was seen, but page does require passwd.
+ * Check that with dummy user:pass */
+ if (authorized < 0)
+ authorized = check_user_passwd(urlcopy, ":");
+ if (!authorized)
+ send_headers_and_exit(HTTP_UNAUTHORIZED);
#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- int server;
-#endif
+ if (found_moved_temporarily) {
+ send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
+ }
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
- const char *s_uid;
- long uid = -1;
+#if ENABLE_FEATURE_HTTPD_PROXY
+ if (proxy_entry != NULL) {
+ int proxy_fd;
+ len_and_sockaddr *lsa;
+
+ proxy_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (proxy_fd < 0)
+ send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+ lsa = host2sockaddr(proxy_entry->host_port, 80);
+ if (lsa == NULL)
+ send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+ if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
+ send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+ fdprintf(proxy_fd, "%s %s%s%s%s HTTP/%c.%c\r\n",
+ prequest, /* GET or POST */
+ proxy_entry->url_to, /* url part 1 */
+ urlcopy + strlen(proxy_entry->url_from), /* url part 2 */
+ (g_query ? "?" : ""), /* "?" (maybe) */
+ (g_query ? g_query : ""), /* query string (maybe) */
+ http_major_version, http_minor_version);
+ header_ptr[0] = '\r';
+ header_ptr[1] = '\n';
+ header_ptr += 2;
+ write(proxy_fd, header_buf, header_ptr - header_buf);
+ free(header_buf); /* on the order of 8k, free it */
+ /* cgi_io_loop_and_exit needs to have two distinct fds */
+ cgi_io_loop_and_exit(proxy_fd, dup(proxy_fd), length);
+ }
#endif
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
- const char *pass;
+ tptr = urlcopy + 1; /* skip first '/' */
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (strncmp(tptr, "cgi-bin/", 8) == 0) {
+ if (tptr[8] == '\0') {
+ /* protect listing "cgi-bin/" */
+ send_headers_and_exit(HTTP_FORBIDDEN);
+ }
+ send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+ }
#endif
- config = xcalloc(1, sizeof(*config));
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- config->realm = "Web Server Authentication";
+ if (urlp[-1] == '/')
+ strcpy(urlp, index_page);
+ if (stat(tptr, &sb) == 0) {
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ char *suffix = strrchr(tptr, '.');
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+ }
+ }
+ }
#endif
+ file_size = sb.st_size;
+ last_mod = sb.st_mtime;
+ }
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (urlp[-1] == '/') {
+ /* It's a dir URL and there is no index.html
+ * Try cgi-bin/index.cgi */
+ if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
+ urlp[0] = '\0';
+ g_query = urlcopy;
+ send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
+ }
+ }
+ /* else fall through to send_file, it errors out if open fails: */
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- config->port = 80;
+ if (prequest != request_GET && prequest != request_HEAD) {
+ /* POST for files does not make sense */
+ send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
+ }
+ send_file_and_exit(tptr,
+ (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)
+ );
+#else
+ send_file_and_exit(tptr, SEND_HEADERS_AND_BODY);
#endif
+}
+
+/*
+ * The main http server function.
+ * Given a socket, listen for new connections and farm out
+ * the processing as a [v]forked process.
+ * Never returns.
+ */
+#if BB_MMU
+static void mini_httpd(int server_socket) NORETURN;
+static void mini_httpd(int server_socket)
+{
+ /* NB: it's best to not use xfuncs in this loop before fork().
+ * Otherwise server may die on transient errors (temporary
+ * out-of-memory condition, etc), which is Bad(tm).
+ * Try to do any dangerous calls after fork.
+ */
+ while (1) {
+ int n;
+ len_and_sockaddr fromAddr;
+
+ /* Wait for connections... */
+ fromAddr.len = LSA_SIZEOF_SA;
+ n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
+
+ if (n < 0)
+ continue;
+ /* set the KEEPALIVE option to cull dead connections */
+ setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ if (fork() == 0) {
+ /* child */
+ /* Do not reload config on HUP */
+ signal(SIGHUP, SIG_IGN);
+ close(server_socket);
+ xmove_fd(n, 0);
+ xdup2(0, 1);
+
+ handle_incoming_and_exit(&fromAddr);
+ }
+ /* parent, or fork failed */
+ close(n);
+ } /* while (1) */
+ /* never reached */
+}
+#else
+static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN;
+static void mini_httpd_nommu(int server_socket, int argc, char **argv)
+{
+ char *argv_copy[argc + 2];
- config->ContentLength = -1;
+ argv_copy[0] = argv[0];
+ argv_copy[1] = (char*)"-i";
+ memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0]));
- opt = bb_getopt_ulflags(argc, argv, httpd_opts,
- &(config->configFile), &url_for_decode, &home_httpd
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
- , &url_for_encode
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
- , &(config->realm)
-# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
- , &pass
-# endif
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- , &s_port
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
- , &s_uid
-#endif
+ /* NB: it's best to not use xfuncs in this loop before vfork().
+ * Otherwise server may die on transient errors (temporary
+ * out-of-memory condition, etc), which is Bad(tm).
+ * Try to do any dangerous calls after fork.
+ */
+ while (1) {
+ int n;
+ len_and_sockaddr fromAddr;
+
+ /* Wait for connections... */
+ fromAddr.len = LSA_SIZEOF_SA;
+ n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
+
+ if (n < 0)
+ continue;
+ /* set the KEEPALIVE option to cull dead connections */
+ setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ if (vfork() == 0) {
+ /* child */
+ /* Do not reload config on HUP */
+ signal(SIGHUP, SIG_IGN);
+ close(server_socket);
+ xmove_fd(n, 0);
+ xdup2(0, 1);
+
+ /* Run a copy of ourself in inetd mode */
+ re_exec(argv_copy);
+ }
+ /* parent, or vfork failed */
+ close(n);
+ } /* while (1) */
+ /* never reached */
+}
#endif
- );
- if(opt & OPT_DECODE_URL) {
- printf("%s", decodeString(url_for_decode, 1));
- return 0;
- }
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
- if(opt & OPT_ENCODE_URL) {
- printf("%s", encodeString(url_for_encode));
- return 0;
- }
+/*
+ * Process a HTTP connection on stdin/out.
+ * Never returns.
+ */
+static void mini_httpd_inetd(void) NORETURN;
+static void mini_httpd_inetd(void)
+{
+ len_and_sockaddr fromAddr;
+
+ memset(&fromAddr, 0, sizeof(fromAddr));
+ fromAddr.len = LSA_SIZEOF_SA;
+ /* NB: can fail if user runs it by hand and types in http cmds */
+ getpeername(0, &fromAddr.u.sa, &fromAddr.len);
+ handle_incoming_and_exit(&fromAddr);
+}
+
+static void sighup_handler(int sig UNUSED_PARAM)
+{
+ parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
+}
+
+enum {
+ c_opt_config_file = 0,
+ d_opt_decode_url,
+ h_opt_home_httpd,
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+ USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
+ USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
+ USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
+ p_opt_port ,
+ p_opt_inetd ,
+ p_opt_foreground,
+ p_opt_verbose ,
+ OPT_CONFIG_FILE = 1 << c_opt_config_file,
+ OPT_DECODE_URL = 1 << d_opt_decode_url,
+ OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
+ OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
+ OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
+ OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
+ OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
+ OPT_PORT = 1 << p_opt_port,
+ OPT_INETD = 1 << p_opt_inetd,
+ OPT_FOREGROUND = 1 << p_opt_foreground,
+ OPT_VERBOSE = 1 << p_opt_verbose,
+};
+
+
+int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int httpd_main(int argc UNUSED_PARAM, char **argv)
+{
+ int server_socket = server_socket; /* for gcc */
+ unsigned opt;
+ char *url_for_decode;
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
+ USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
+ USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
+ USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
+
+ INIT_G();
+
+#if ENABLE_LOCALE_SUPPORT
+ /* Undo busybox.c: we want to speak English in http (dates etc) */
+ setlocale(LC_TIME, "C");
+#endif
+
+ home_httpd = xrealloc_getcwd_or_warn(NULL);
+ /* -v counts, -i implies -f */
+ opt_complementary = "vv:if";
+ /* We do not "absolutize" path given by -h (home) opt.
+ * If user gives relative path in -h,
+ * $SCRIPT_FILENAME will not be set. */
+ opt = getopt32(argv, "c:d:h:"
+ USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+ USE_FEATURE_HTTPD_BASIC_AUTH("r:")
+ USE_FEATURE_HTTPD_AUTH_MD5("m:")
+ USE_FEATURE_HTTPD_SETUID("u:")
+ "p:ifv",
+ &opt_c_configFile, &url_for_decode, &home_httpd
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+ USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
+ USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
+ USE_FEATURE_HTTPD_SETUID(, &s_ugid)
+ , &bind_addr_or_port
+ , &verbose
+ );
+ if (opt & OPT_DECODE_URL) {
+ fputs(decodeString(url_for_decode, 1), stdout);
+ return 0;
+ }
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+ if (opt & OPT_ENCODE_URL) {
+ fputs(encodeString(url_for_encode), stdout);
+ return 0;
+ }
#endif
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
- if(opt & OPT_MD5) {
- printf("%s\n", pw_encrypt(pass, "$1$"));
- return 0;
- }
+#if ENABLE_FEATURE_HTTPD_AUTH_MD5
+ if (opt & OPT_MD5) {
+ puts(pw_encrypt(pass, "$1$", 1));
+ return 0;
+ }
#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- if(opt & OPT_PORT)
- config->port = bb_xgetlarg(s_port, 10, 1, 0xffff);
- config->debugHttpd = opt & OPT_DEBUG;
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
- if(opt & OPT_SETUID) {
- char *e;
-
- uid = strtol(s_uid, &e, 0);
- if(*e != '\0') {
- /* not integer */
- uid = my_getpwnam(s_uid);
+#if ENABLE_FEATURE_HTTPD_SETUID
+ if (opt & OPT_SETUID) {
+ xget_uidgid(&ugid, s_ugid);
}
- }
#endif
+
+#if !BB_MMU
+ if (!(opt & OPT_FOREGROUND)) {
+ bb_daemonize_or_rexec(0, argv); /* don't change current directory */
+ }
#endif
- if(chdir(home_httpd)) {
- bb_perror_msg_and_die("can`t chdir to %s", home_httpd);
- }
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- server = openServer();
-# ifdef CONFIG_FEATURE_HTTPD_SETUID
- /* drop privilegies */
- if(uid > 0)
- setuid(uid);
-# endif
-# ifdef CONFIG_FEATURE_HTTPD_CGI
- addEnvPort("SERVER");
-# endif
+ xchdir(home_httpd);
+ if (!(opt & OPT_INETD)) {
+ signal(SIGCHLD, SIG_IGN);
+ server_socket = openServer();
+#if ENABLE_FEATURE_HTTPD_SETUID
+ /* drop privileges */
+ if (opt & OPT_SETUID) {
+ if (ugid.gid != (gid_t)-1) {
+ if (setgroups(1, &ugid.gid) == -1)
+ bb_perror_msg_and_die("setgroups");
+ xsetgid(ugid.gid);
+ }
+ xsetuid(ugid.uid);
+ }
#endif
+ }
-#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
- sighup_handler(0);
-#else
- parse_conf(default_path_httpd_conf, FIRST_PARSE);
+#if 0
+ /* User can do it himself: 'env - PATH="$PATH" httpd'
+ * We don't do it because we don't want to screw users
+ * which want to do
+ * 'env - VAR1=val1 VAR2=val2 httpd'
+ * and have VAR1 and VAR2 values visible in their CGIs.
+ * Besides, it is also smaller. */
+ {
+ char *p = getenv("PATH");
+ /* env strings themself are not freed, no need to xstrdup(p): */
+ clearenv();
+ if (p)
+ putenv(p - 5);
+// if (!(opt & OPT_INETD))
+// setenv_long("SERVER_PORT", ???);
+ }
#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
- if (!config->debugHttpd) {
- if (daemon(1, 0) < 0) /* don`t change curent directory */
- bb_perror_msg_and_die("daemon");
- }
- return miniHttpd(server);
+ parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
+ if (!(opt & OPT_INETD))
+ signal(SIGHUP, sighup_handler);
+
+ xfunc_error_retval = 0;
+ if (opt & OPT_INETD)
+ mini_httpd_inetd();
+#if BB_MMU
+ if (!(opt & OPT_FOREGROUND))
+ bb_daemonize(0); /* don't change current directory */
+ mini_httpd(server_socket); /* never returns */
#else
- return miniHttpd();
+ mini_httpd_nommu(server_socket, argc, argv); /* never returns */
#endif
+ /* return 0; */
}
diff --git a/release/src/router/busybox/networking/httpd_indexcgi.c b/release/src/router/busybox/networking/httpd_indexcgi.c
new file mode 100644
index 00000000..94c6a692
--- /dev/null
+++ b/release/src/router/busybox/networking/httpd_indexcgi.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * This program is a CGI application. It outputs directory index page.
+ * Put it into cgi-bin/index.cgi and chmod 0755.
+ */
+
+/* Build a-la
+i486-linux-uclibc-gcc \
+-static -static-libgcc \
+-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \
+-Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \
+-Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \
+-Wmissing-prototypes -Wmissing-declarations \
+-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \
+-ffunction-sections -fdata-sections -fno-guess-branch-probability \
+-funsigned-char \
+-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \
+-march=i386 -mpreferred-stack-boundary=2 \
+-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \
+httpd_indexcgi.c -o index.cgi
+*/
+
+/* We don't use printf, as it pulls in >12 kb of code from uclibc (i386). */
+/* Currently malloc machinery is the biggest part of libc we pull in. */
+/* We have only one realloc and one strdup, any idea how to do without? */
+/* Size (i386, approximate):
+ * text data bss dec hex filename
+ * 13036 44 3052 16132 3f04 index.cgi
+ * 2576 4 2048 4628 1214 index.cgi.o
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
+
+/* Appearance of the table is controlled by style sheet *ONLY*,
+ * formatting code uses <TAG class=CLASS> to apply style
+ * to elements. Edit stylesheet to your liking and recompile. */
+
+#define STYLE_STR \
+"<style>" "\n"\
+"table {" "\n"\
+ "width:100%;" "\n"\
+ "background-color:#fff5ee;" "\n"\
+ "border-width:1px;" /* 1px 1px 1px 1px; */ "\n"\
+ "border-spacing:2px;" "\n"\
+ "border-style:solid;" /* solid solid solid solid; */ "\n"\
+ "border-color:black;" /* black black black black; */ "\n"\
+ "border-collapse:collapse;" "\n"\
+"}" "\n"\
+"th {" "\n"\
+ "border-width:1px;" /* 1px 1px 1px 1px; */ "\n"\
+ "padding:1px;" /* 1px 1px 1px 1px; */ "\n"\
+ "border-style:solid;" /* solid solid solid solid; */ "\n"\
+ "border-color:black;" /* black black black black; */ "\n"\
+"}" "\n"\
+"td {" "\n"\
+ /* top right bottom left */ \
+ "border-width:0px 1px 0px 1px;" "\n"\
+ "padding:1px;" /* 1px 1px 1px 1px; */ "\n"\
+ "border-style:solid;" /* solid solid solid solid; */ "\n"\
+ "border-color:black;" /* black black black black; */ "\n"\
+ "white-space:nowrap;" "\n"\
+"}" "\n"\
+"tr.hdr { background-color:#eee5de; }" "\n"\
+"tr.o { background-color:#ffffff; }" "\n"\
+/* tr.e { ... } - for even rows (currently none) */ \
+"tr.foot { background-color:#eee5de; }" "\n"\
+"th.cnt { text-align:left; }" "\n"\
+"th.sz { text-align:right; }" "\n"\
+"th.dt { text-align:right; }" "\n"\
+"td.sz { text-align:right; }" "\n"\
+"td.dt { text-align:right; }" "\n"\
+"col.nm { width:98%; }" "\n"\
+"col.sz { width:1%; }" "\n"\
+"col.dt { width:1%; }" "\n"\
+"</style>" "\n"\
+
+typedef struct dir_list_t {
+ char *dl_name;
+ mode_t dl_mode;
+ off_t dl_size;
+ time_t dl_mtime;
+} dir_list_t;
+
+static int compare_dl(dir_list_t *a, dir_list_t *b)
+{
+ /* ".." is 'less than' any other dir entry */
+ if (strcmp(a->dl_name, "..") == 0) {
+ return -1;
+ }
+ if (strcmp(b->dl_name, "..") == 0) {
+ return 1;
+ }
+ if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) {
+ /* 1 if b is a dir (and thus a is 'after' b, a > b),
+ * else -1 (a < b) */
+ return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1;
+ }
+ return strcmp(a->dl_name, b->dl_name);
+}
+
+static char buffer[2*1024 > sizeof(STYLE_STR) ? 2*1024 : sizeof(STYLE_STR)];
+static char *dst = buffer;
+enum {
+ BUFFER_SIZE = sizeof(buffer),
+ HEADROOM = 64,
+};
+
+/* After this call, you have at least size + HEADROOM bytes available
+ * ahead of dst */
+static void guarantee(int size)
+{
+ if (buffer + (BUFFER_SIZE-HEADROOM) - dst >= size)
+ return;
+ write(STDOUT_FILENO, buffer, dst - buffer);
+ dst = buffer;
+}
+
+/* NB: formatters do not store terminating NUL! */
+
+/* HEADROOM bytes are available after dst after this call */
+static void fmt_str(/*char *dst,*/ const char *src)
+{
+ unsigned len = strlen(src);
+ guarantee(len);
+ memcpy(dst, src, len);
+ dst += len;
+}
+
+/* HEADROOM bytes after dst are available after this call */
+static void fmt_url(/*char *dst,*/ const char *name)
+{
+ while (*name) {
+ unsigned c = *name++;
+ guarantee(3);
+ *dst = c;
+ if ((c - '0') > 9 /* not a digit */
+ && ((c|0x20) - 'a') > 26 /* not A-Z or a-z */
+ && !strchr("._-+@", c)
+ ) {
+ *dst++ = '%';
+ *dst++ = "0123456789ABCDEF"[c >> 4];
+ *dst = "0123456789ABCDEF"[c & 0xf];
+ }
+ dst++;
+ }
+}
+
+/* HEADROOM bytes are available after dst after this call */
+static void fmt_html(/*char *dst,*/ const char *name)
+{
+ while (*name) {
+ char c = *name++;
+ if (c == '<')
+ fmt_str("&lt;");
+ else if (c == '>')
+ fmt_str("&gt;");
+ else if (c == '&') {
+ fmt_str("&amp;");
+ } else {
+ guarantee(1);
+ *dst++ = c;
+ continue;
+ }
+ }
+}
+
+/* HEADROOM bytes are available after dst after this call */
+static void fmt_ull(/*char *dst,*/ unsigned long long n)
+{
+ char buf[sizeof(n)*3 + 2];
+ char *p;
+
+ p = buf + sizeof(buf) - 1;
+ *p = '\0';
+ do {
+ *--p = (n % 10) + '0';
+ n /= 10;
+ } while (n);
+ fmt_str(/*dst,*/ p);
+}
+
+/* Does not call guarantee - eats into headroom instead */
+static void fmt_02u(/*char *dst,*/ unsigned n)
+{
+ /* n %= 100; - not needed, callers don't pass big n */
+ dst[0] = (n / 10) + '0';
+ dst[1] = (n % 10) + '0';
+ dst += 2;
+}
+
+/* Does not call guarantee - eats into headroom instead */
+static void fmt_04u(/*char *dst,*/ unsigned n)
+{
+ /* n %= 10000; - not needed, callers don't pass big n */
+ fmt_02u(n / 100);
+ fmt_02u(n % 100);
+}
+
+int main(void)
+{
+ dir_list_t *dir_list;
+ dir_list_t *cdir;
+ unsigned dir_list_count;
+ unsigned count_dirs;
+ unsigned count_files;
+ unsigned long long size_total;
+ int odd;
+ DIR *dirp;
+ char *QUERY_STRING;
+
+ QUERY_STRING = getenv("QUERY_STRING");
+ if (!QUERY_STRING
+ || QUERY_STRING[0] != '/'
+ || strstr(QUERY_STRING, "/../")
+ || strcmp(strrchr(QUERY_STRING, '/'), "/..") == 0
+ ) {
+ return 1;
+ }
+
+ if (chdir("..")
+ || (QUERY_STRING[1] && chdir(QUERY_STRING + 1))
+ ) {
+ return 1;
+ }
+
+ dirp = opendir(".");
+ if (!dirp)
+ return 1;
+ dir_list = NULL;
+ dir_list_count = 0;
+ while (1) {
+ struct dirent *dp;
+ struct stat sb;
+
+ dp = readdir(dirp);
+ if (!dp)
+ break;
+ if (dp->d_name[0] == '.' && !dp->d_name[1])
+ continue;
+ if (stat(dp->d_name, &sb) != 0)
+ continue;
+ dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0]));
+ dir_list[dir_list_count].dl_name = strdup(dp->d_name);
+ dir_list[dir_list_count].dl_mode = sb.st_mode;
+ dir_list[dir_list_count].dl_size = sb.st_size;
+ dir_list[dir_list_count].dl_mtime = sb.st_mtime;
+ dir_list_count++;
+ }
+ closedir(dirp);
+
+ qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl);
+
+ fmt_str(
+ "" /* Additional headers (currently none) */
+ "\r\n" /* Mandatory empty line after headers */
+ "<html><head><title>Index of ");
+ /* Guard against directories with &, > etc */
+ fmt_html(QUERY_STRING);
+ fmt_str(
+ "</title>\n"
+ STYLE_STR
+ "</head>" "\n"
+ "<body>" "\n"
+ "<h1>Index of ");
+ fmt_html(QUERY_STRING);
+ fmt_str(
+ "</h1>" "\n"
+ "<table>" "\n"
+ "<col class=nm><col class=sz><col class=dt>" "\n"
+ "<tr class=hdr><th class=cnt>Name<th class=sz>Size<th class=dt>Last modified" "\n");
+
+ odd = 0;
+ count_dirs = 0;
+ count_files = 0;
+ size_total = 0;
+ cdir = dir_list;
+ while (dir_list_count--) {
+ struct tm *tm;
+
+ if (S_ISDIR(cdir->dl_mode)) {
+ count_dirs++;
+ } else if (S_ISREG(cdir->dl_mode)) {
+ count_files++;
+ size_total += cdir->dl_size;
+ } else
+ goto next;
+
+ fmt_str("<tr class=");
+ *dst++ = (odd ? 'o' : 'e');
+ fmt_str("><td class=nm><a href='");
+ fmt_url(cdir->dl_name); /* %20 etc */
+ if (S_ISDIR(cdir->dl_mode))
+ *dst++ = '/';
+ fmt_str("'>");
+ fmt_html(cdir->dl_name); /* &lt; etc */
+ if (S_ISDIR(cdir->dl_mode))
+ *dst++ = '/';
+ fmt_str("</a><td class=sz>");
+ if (S_ISREG(cdir->dl_mode))
+ fmt_ull(cdir->dl_size);
+ fmt_str("<td class=dt>");
+ tm = gmtime(&cdir->dl_mtime);
+ fmt_04u(1900 + tm->tm_year); *dst++ = '-';
+ fmt_02u(tm->tm_mon + 1); *dst++ = '-';
+ fmt_02u(tm->tm_mday); *dst++ = ' ';
+ fmt_02u(tm->tm_hour); *dst++ = ':';
+ fmt_02u(tm->tm_min); *dst++ = ':';
+ fmt_02u(tm->tm_sec);
+ *dst++ = '\n';
+
+ odd = 1 - odd;
+ next:
+ cdir++;
+ }
+
+ fmt_str("<tr class=foot><th class=cnt>Files: ");
+ fmt_ull(count_files);
+ /* count_dirs - 1: we don't want to count ".." */
+ fmt_str(", directories: ");
+ fmt_ull(count_dirs - 1);
+ fmt_str("<th class=sz>");
+ fmt_ull(size_total);
+ fmt_str("<th class=dt>\n");
+ /* "</table></body></html>" - why bother? */
+ guarantee(BUFFER_SIZE * 2); /* flush */
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/httpd_post_upload.txt b/release/src/router/busybox/networking/httpd_post_upload.txt
new file mode 100644
index 00000000..a53b1146
--- /dev/null
+++ b/release/src/router/busybox/networking/httpd_post_upload.txt
@@ -0,0 +1,76 @@
+POST upload example:
+
+post_upload.htm
+===============
+<html>
+<body>
+<form action=/cgi-bin/post_upload.cgi method=post enctype=multipart/form-data>
+File to upload: <input type=file name=file1> <input type=submit>
+</form>
+
+
+post_upload.cgi
+===============
+#!/bin/sh
+
+# POST upload format:
+# -----------------------------29995809218093749221856446032^M
+# Content-Disposition: form-data; name="file1"; filename="..."^M
+# Content-Type: application/octet-stream^M
+# ^M <--------- headers end with empty line
+# file contents
+# file contents
+# file contents
+# ^M <--------- extra empty line
+# -----------------------------29995809218093749221856446032--^M
+
+# Beware: bashism $'\r' is used to handle ^M
+
+file=/tmp/$$-$RANDOM
+
+# CGI output must start with at least empty line (or headers)
+printf '\r\n'
+
+IFS=$'\r'
+read -r delim_line
+
+IFS=''
+delim_line="${delim_line}--"$'\r'
+
+while read -r line; do
+ test "$line" = '' && break
+ test "$line" = $'\r' && break
+done
+
+# This will not work well for binary files: bash 3.2 is upset
+# by reading NUL bytes and loses chunks of data.
+# If you are not bothered by having junk appended to the uploaded file,
+# consider using simple "cat >file" instead of the entire
+# fragment below.
+
+while read -r line; do
+
+ while test "$line" = $'\r'; do
+ read -r line
+ test "$line" = "$delim_line" && {
+ # Aha! Empty line + delimiter! All done
+ cat <<EOF
+<html>
+<body>
+File upload has been accepted
+EOF
+ exit 0
+ }
+ # Empty line + NOT delimiter. Save empty line,
+ # and go check next line
+ printf "%s\n" $'\r' -vC >&3
+ done
+ # Not empty line - just save
+ printf "%s\n" "$line" -vC >&3
+done 3>"$file"
+
+cat <<EOF
+<html>
+<body>
+File upload was not terminated with '$delim_line' - ??!
+EOF
diff --git a/release/src/router/busybox/networking/ifconfig.c b/release/src/router/busybox/networking/ifconfig.c
index edaeaff3..22b1682b 100644
--- a/release/src/router/busybox/networking/ifconfig.c
+++ b/release/src/router/busybox/networking/ifconfig.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/* ifconfig
*
* Similar to the standard Unix ifconfig, but with only the necessary
@@ -6,17 +7,10 @@
* Bjorn Wesen, Axis Communications AB
*
*
- * Authors of the original ifconfig was:
+ * Authors of the original ifconfig was:
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
*
- * This program is free software; you can redistribute it
- * and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * $Id: ifconfig.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
- *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/*
@@ -32,26 +26,20 @@
* IPV6 support added by Bart Visscher <magick@linux-fan.com>
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h> /* strcmp and friends */
-#include <ctype.h> /* isdigit and friends */
-#include <stddef.h> /* offsetof */
-#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
-#include <asm/types.h>
-#include <linux/if_ether.h>
+#include <sys/types.h>
+#include <netinet/if_ether.h>
#endif
#include "inet_common.h"
-#include "busybox.h"
+#include "libbb.h"
-#ifdef CONFIG_FEATURE_IFCONFIG_SLIP
+#if ENABLE_FEATURE_IFCONFIG_SLIP
# include <net/if_slip.h>
#endif
@@ -74,7 +62,7 @@
# define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
#endif
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
struct in6_ifreq {
struct in6_addr ifr6_addr;
uint32_t ifr6_prefixlen;
@@ -125,7 +113,7 @@ struct in6_ifreq {
#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */
#define A_SET_AFTER 0x40 /* Set a flag at the end. */
#define A_COLON_CHK 0x80 /* Is this needed? See below. */
-#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
#define A_HOSTNAME 0x100 /* Set if it is ip addr. */
#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */
#else
@@ -163,7 +151,7 @@ struct in6_ifreq {
#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
-#define ARG_POINTOPOINT (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR)
#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR)
#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
@@ -182,7 +170,7 @@ struct arg1opt {
struct options {
const char *name;
-#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
const unsigned int flags:6;
const unsigned int arg_flags:10;
#else
@@ -195,105 +183,95 @@ struct options {
#define ifreq_offsetof(x) offsetof(struct ifreq, x)
static const struct arg1opt Arg1Opt[] = {
- {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)},
- {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)},
- {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)},
- {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
- {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
- {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
- {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)},
-#endif
- {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+ { "SIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric) },
+ { "SIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu) },
+ { "SIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen) },
+ { "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) },
+ { "SIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask) },
+ { "SIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr) },
+#if ENABLE_FEATURE_IFCONFIG_HW
+ { "SIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr) },
+#endif
+ { "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) },
#ifdef SIOCSKEEPALIVE
- {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
+ { "SKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data) },
#endif
#ifdef SIOCSOUTFILL
- {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)},
+ { "SOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data) },
#endif
-#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
- {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)},
- {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)},
- {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)},
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start) },
+ { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr) },
+ { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq) },
#endif
/* Last entry if for unmatched (possibly hostname) arg. */
-#ifdef CONFIG_FEATURE_IPV6
- {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
- {"SIOCDIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+#if ENABLE_FEATURE_IPV6
+ { "SIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */
+ { "DIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */
#endif
- {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)},
+ { "SIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr) },
};
static const struct options OptArray[] = {
- {"metric", N_ARG, ARG_METRIC, 0},
- {"mtu", N_ARG, ARG_MTU, 0},
- {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0},
- {"dstaddr", N_ARG, ARG_DSTADDR, 0},
- {"netmask", N_ARG, ARG_NETMASK, 0},
- {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST},
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
- {"hw", N_ARG, ARG_HW, 0},
-#endif
- {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
+ { "metric", N_ARG, ARG_METRIC, 0 },
+ { "mtu", N_ARG, ARG_MTU, 0 },
+ { "txqueuelen", N_ARG, ARG_TXQUEUELEN, 0 },
+ { "dstaddr", N_ARG, ARG_DSTADDR, 0 },
+ { "netmask", N_ARG, ARG_NETMASK, 0 },
+ { "broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST },
+#if ENABLE_FEATURE_IFCONFIG_HW
+ { "hw", N_ARG, ARG_HW, 0 },
+#endif
+ { "pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT },
#ifdef SIOCSKEEPALIVE
- {"keepalive", N_ARG, ARG_KEEPALIVE, 0},
+ { "keepalive", N_ARG, ARG_KEEPALIVE, 0 },
#endif
#ifdef SIOCSOUTFILL
- {"outfill", N_ARG, ARG_OUTFILL, 0},
-#endif
-#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
- {"mem_start", N_ARG, ARG_MEM_START, 0},
- {"io_addr", N_ARG, ARG_IO_ADDR, 0},
- {"irq", N_ARG, ARG_IRQ, 0},
-#endif
-#ifdef CONFIG_FEATURE_IPV6
- {"add", N_ARG, ARG_ADD_DEL, 0},
- {"del", N_ARG, ARG_ADD_DEL, 0},
-#endif
- {"arp", N_CLR | M_SET, 0, IFF_NOARP},
- {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS},
- {"promisc", N_SET | M_CLR, 0, IFF_PROMISC},
- {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST},
- {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI},
- {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC},
- {"up", N_SET, 0, (IFF_UP | IFF_RUNNING)},
- {"down", N_CLR, 0, IFF_UP},
- {NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)}
+ { "outfill", N_ARG, ARG_OUTFILL, 0 },
+#endif
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ { "mem_start", N_ARG, ARG_MEM_START, 0 },
+ { "io_addr", N_ARG, ARG_IO_ADDR, 0 },
+ { "irq", N_ARG, ARG_IRQ, 0 },
+#endif
+#if ENABLE_FEATURE_IPV6
+ { "add", N_ARG, ARG_ADD_DEL, 0 },
+ { "del", N_ARG, ARG_ADD_DEL, 0 },
+#endif
+ { "arp", N_CLR | M_SET, 0, IFF_NOARP },
+ { "trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS },
+ { "promisc", N_SET | M_CLR, 0, IFF_PROMISC },
+ { "multicast", N_SET | M_CLR, 0, IFF_MULTICAST },
+ { "allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI },
+ { "dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC },
+ { "up", N_SET, 0, (IFF_UP | IFF_RUNNING) },
+ { "down", N_CLR, 0, IFF_UP },
+ { NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING) }
};
/*
* A couple of prototypes.
*/
-
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
-static int in_ether(char *bufp, struct sockaddr *sap);
-#endif
-
-#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
-extern int interface_opt_a;
-extern int display_interfaces(char *ifname);
+#if ENABLE_FEATURE_IFCONFIG_HW
+static int in_ether(const char *bufp, struct sockaddr *sap);
#endif
/*
* Our main function.
*/
-
+int ifconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ifconfig_main(int argc, char **argv)
{
struct ifreq ifr;
struct sockaddr_in sai;
-#ifdef CONFIG_FEATURE_IPV6
- struct sockaddr_in6 sai6;
-#endif
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
+#if ENABLE_FEATURE_IFCONFIG_HW
struct sockaddr sa;
#endif
const struct arg1opt *a1op;
const struct options *op;
int sockfd; /* socket fd we use to manipulate stuff with */
- int goterr;
int selector;
-#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
unsigned int mask;
unsigned int did_flags;
unsigned int sai_hostname, sai_netmask;
@@ -302,11 +280,11 @@ int ifconfig_main(int argc, char **argv)
unsigned char did_flags;
#endif
char *p;
- char host[128];
+ /*char host[128];*/
+ const char *host = NULL; /* make gcc happy */
- goterr = 0;
did_flags = 0;
-#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
sai_hostname = 0;
sai_netmask = 0;
#endif
@@ -315,8 +293,8 @@ int ifconfig_main(int argc, char **argv)
++argv;
--argc;
-#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
- if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) {
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+ if (argc > 0 && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) {
interface_opt_a = 1;
--argc;
++argv;
@@ -324,21 +302,18 @@ int ifconfig_main(int argc, char **argv)
#endif
if (argc <= 1) {
-#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+#if ENABLE_FEATURE_IFCONFIG_STATUS
return display_interfaces(argc ? *argv : NULL);
#else
- bb_error_msg_and_die
- ("ifconfig was not compiled with interface status display support.");
+ bb_error_msg_and_die("no support for status display");
#endif
}
/* Create a channel to the NET kernel. */
- if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- bb_perror_msg_and_die("socket");
- }
+ sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
/* get interface name */
- safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+ strncpy_IFNAMSIZ(ifr.ifr_name, *argv);
/* Process the remaining arguments. */
while (*++argv != (char *) NULL) {
@@ -350,221 +325,188 @@ int ifconfig_main(int argc, char **argv)
}
for (op = OptArray; op->name; op++) { /* Find table entry. */
if (strcmp(p, op->name) == 0) { /* If name matches... */
- if ((mask &= op->flags)) { /* set the mask and go. */
- goto FOUND_ARG;;
- }
+ mask &= op->flags;
+ if (mask) /* set the mask and go. */
+ goto FOUND_ARG;
/* If we get here, there was a valid arg with an */
/* invalid '-' prefix. */
- ++goterr;
- goto LOOP;
+ bb_error_msg_and_die("bad: '%s'", p-1);
}
}
/* We fell through, so treat as possible hostname. */
- a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
+ a1op = Arg1Opt + ARRAY_SIZE(Arg1Opt) - 1;
mask = op->arg_flags;
goto HOSTNAME;
- FOUND_ARG:
+ FOUND_ARG:
if (mask & ARG_MASK) {
mask = op->arg_flags;
a1op = Arg1Opt + (op - OptArray);
- if (mask & A_NETMASK & did_flags) {
+ if (mask & A_NETMASK & did_flags)
bb_show_usage();
- }
if (*++argv == NULL) {
- if (mask & A_ARG_REQ) {
+ if (mask & A_ARG_REQ)
bb_show_usage();
- } else {
- --argv;
- mask &= A_SET_AFTER; /* just for broadcast */
- }
+ --argv;
+ mask &= A_SET_AFTER; /* just for broadcast */
} else { /* got an arg so process it */
- HOSTNAME:
+ HOSTNAME:
did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
if (mask & A_CAST_HOST_COPY) {
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
+#if ENABLE_FEATURE_IFCONFIG_HW
if (mask & A_CAST_RESOLVE) {
#endif
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
char *prefix;
int prefix_len = 0;
#endif
-
- safe_strncpy(host, *argv, (sizeof host));
-#ifdef CONFIG_FEATURE_IPV6
- if ((prefix = strchr(host, '/'))) {
- prefix_len = atol(prefix + 1);
- if ((prefix_len < 0) || (prefix_len > 128)) {
- ++goterr;
- goto LOOP;
- }
- *prefix = 0;
+ /*safe_strncpy(host, *argv, (sizeof host));*/
+ host = *argv;
+#if ENABLE_FEATURE_IPV6
+ prefix = strchr(host, '/');
+ if (prefix) {
+ prefix_len = xatou_range(prefix + 1, 0, 128);
+ *prefix = '\0';
}
#endif
-
sai.sin_family = AF_INET;
sai.sin_port = 0;
- if (!strcmp(host, bb_INET_default)) {
+ if (!strcmp(host, bb_str_default)) {
/* Default is special, meaning 0.0.0.0. */
sai.sin_addr.s_addr = INADDR_ANY;
-#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
- } else if (((host[0] == '+') && !host[1]) && (mask & A_BROADCAST) &&
- (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)) {
+ }
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ else if ((host[0] == '+' && !host[1]) && (mask & A_BROADCAST)
+ && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)
+ ) {
/* + is special, meaning broadcast is derived. */
sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
+ }
#endif
-#ifdef CONFIG_FEATURE_IPV6
- } else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) {
- int sockfd6;
- struct in6_ifreq ifr6;
-
- memcpy((char *) &ifr6.ifr6_addr,
- (char *) &sai6.sin6_addr,
- sizeof(struct in6_addr));
-
- /* Create a channel to the NET kernel. */
- if ((sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- bb_perror_msg_and_die("socket6");
- }
- if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) {
- perror("SIOGIFINDEX");
- ++goterr;
+ else {
+ len_and_sockaddr *lsa;
+ if (strcmp(host, "inet") == 0)
+ continue; /* compat stuff */
+ lsa = xhost2sockaddr(host, 0);
+#if ENABLE_FEATURE_IPV6
+ if (lsa->u.sa.sa_family == AF_INET6) {
+ int sockfd6;
+ struct in6_ifreq ifr6;
+
+ memcpy((char *) &ifr6.ifr6_addr,
+ (char *) &(lsa->u.sin6.sin6_addr),
+ sizeof(struct in6_addr));
+
+ /* Create a channel to the NET kernel. */
+ sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
+ xioctl(sockfd6, SIOGIFINDEX, &ifr);
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = prefix_len;
+ ioctl_or_perror_and_die(sockfd6, a1op->selector, &ifr6, "SIOC%s", a1op->name);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
continue;
}
- ifr6.ifr6_ifindex = ifr.ifr_ifindex;
- ifr6.ifr6_prefixlen = prefix_len;
- if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) {
- perror(a1op->name);
- ++goterr;
- }
- continue;
#endif
- } else if (inet_aton(host, &sai.sin_addr) == 0) {
- /* It's not a dotted quad. */
- ++goterr;
- continue;
+ sai.sin_addr = lsa->u.sin.sin_addr;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
}
-#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
- if (mask & A_HOSTNAME) {
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ if (mask & A_HOSTNAME)
sai_hostname = sai.sin_addr.s_addr;
- }
- if (mask & A_NETMASK) {
+ if (mask & A_NETMASK)
sai_netmask = sai.sin_addr.s_addr;
- }
#endif
p = (char *) &sai;
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
+#if ENABLE_FEATURE_IFCONFIG_HW
} else { /* A_CAST_HOST_COPY_IN_ETHER */
/* This is the "hw" arg case. */
- if (strcmp("ether", *argv) || (*++argv == NULL)) {
+ smalluint hw_class= index_in_substrings("ether\0"
+ USE_FEATURE_HWIB("infiniband\0"), *argv) + 1;
+ if (!hw_class || !*++argv)
bb_show_usage();
- }
- safe_strncpy(host, *argv, (sizeof host));
- if (in_ether(host, &sa)) {
- bb_error_msg("invalid hw-addr %s", host);
- ++goterr;
- continue;
- }
+ /*safe_strncpy(host, *argv, sizeof(host));*/
+ host = *argv;
+ if (hw_class == 1 ? in_ether(host, &sa) : in_ib(host, &sa))
+ bb_error_msg_and_die("invalid hw-addr %s", host);
p = (char *) &sa;
}
#endif
- memcpy((((char *) (&ifr)) + a1op->ifr_offset),
+ memcpy( (((char *)&ifr) + a1op->ifr_offset),
p, sizeof(struct sockaddr));
} else {
- unsigned int i = strtoul(*argv, NULL, 0);
-
- p = ((char *) (&ifr)) + a1op->ifr_offset;
-#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ /* FIXME: error check?? */
+ unsigned long i = strtoul(*argv, NULL, 0);
+ p = ((char *)&ifr) + a1op->ifr_offset;
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
if (mask & A_MAP_TYPE) {
- if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) {
- ++goterr;
- continue;
- }
- if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) {
+ xioctl(sockfd, SIOCGIFMAP, &ifr);
+ if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR)
*((unsigned char *) p) = i;
- } else if (mask & A_MAP_USHORT) {
+ else if (mask & A_MAP_USHORT)
*((unsigned short *) p) = i;
- } else {
+ else
*((unsigned long *) p) = i;
- }
} else
#endif
- if (mask & A_CAST_CHAR_PTR) {
+ if (mask & A_CAST_CHAR_PTR)
*((caddr_t *) p) = (caddr_t) i;
- } else { /* A_CAST_INT */
+ else /* A_CAST_INT */
*((int *) p) = i;
- }
}
- if (ioctl(sockfd, a1op->selector, &ifr) < 0) {
- perror(a1op->name);
- ++goterr;
- continue;
- }
+ ioctl_or_perror_and_die(sockfd, a1op->selector, &ifr, "SIOC%s", a1op->name);
#ifdef QUESTIONABLE_ALIAS_CASE
if (mask & A_COLON_CHK) {
/*
* Don't do the set_flag() if the address is an alias with
- * a - at the end, since it's deleted already! - Roman
+ * a '-' at the end, since it's deleted already! - Roman
*
* Should really use regex.h here, not sure though how well
- * it'll go with the cross-platform support etc.
+ * it'll go with the cross-platform support etc.
*/
char *ptr;
short int found_colon = 0;
-
- for (ptr = ifr.ifr_name; *ptr; ptr++) {
- if (*ptr == ':') {
+ for (ptr = ifr.ifr_name; *ptr; ptr++)
+ if (*ptr == ':')
found_colon++;
- }
- }
-
- if (found_colon && *(ptr - 1) == '-') {
+ if (found_colon && ptr[-1] == '-')
continue;
- }
}
#endif
}
- if (!(mask & A_SET_AFTER)) {
+ if (!(mask & A_SET_AFTER))
continue;
- }
mask = N_SET;
}
- if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
- perror("SIOCGIFFLAGS");
- ++goterr;
- } else {
- selector = op->selector;
- if (mask & SET_MASK) {
- ifr.ifr_flags |= selector;
- } else {
- ifr.ifr_flags &= ~selector;
- }
- if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
- perror("SIOCSIFFLAGS");
- ++goterr;
- }
- }
- LOOP:
- continue;
- } /* end of while-loop */
-
- return goterr;
+ xioctl(sockfd, SIOCGIFFLAGS, &ifr);
+ selector = op->selector;
+ if (mask & SET_MASK)
+ ifr.ifr_flags |= selector;
+ else
+ ifr.ifr_flags &= ~selector;
+ xioctl(sockfd, SIOCSIFFLAGS, &ifr);
+ } /* while () */
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(sockfd);
+ return 0;
}
-#ifdef CONFIG_FEATURE_IFCONFIG_HW
+#if ENABLE_FEATURE_IFCONFIG_HW
/* Input an Ethernet address and convert to binary. */
-static int in_ether(char *bufp, struct sockaddr *sap)
+static int in_ether(const char *bufp, struct sockaddr *sap)
{
- unsigned char *ptr;
+ char *ptr;
int i, j;
unsigned char val;
unsigned char c;
sap->sa_family = ARPHRD_ETHER;
- ptr = sap->sa_data;
+ ptr = (char *) sap->sa_data;
i = 0;
do {
@@ -593,6 +535,6 @@ static int in_ether(char *bufp, struct sockaddr *sap)
*ptr++ = val;
} while (++i < ETH_ALEN);
- return (int) (*bufp); /* Error if we don't end at end of string. */
+ return *bufp; /* Error if we don't end at end of string. */
}
#endif
diff --git a/release/src/router/busybox/networking/ifenslave.c b/release/src/router/busybox/networking/ifenslave.c
new file mode 100644
index 00000000..fa226425
--- /dev/null
+++ b/release/src/router/busybox/networking/ifenslave.c
@@ -0,0 +1,592 @@
+/* Mode: C;
+ *
+ * Mini ifenslave implementation for busybox
+ * Copyright (C) 2005 by Marc Leeman <marc.leeman@barco.com>
+ *
+ * ifenslave.c: Configure network interfaces for parallel routing.
+ *
+ * This program controls the Linux implementation of running multiple
+ * network interfaces in parallel.
+ *
+ * Author: Donald Becker <becker@cesdis.gsfc.nasa.gov>
+ * Copyright 1994-1996 Donald Becker
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ * Center of Excellence in Space Data and Information Sciences
+ * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+ *
+ * Changes :
+ * - 2000/10/02 Willy Tarreau <willy at meta-x.org> :
+ * - few fixes. Master's MAC address is now correctly taken from
+ * the first device when not previously set ;
+ * - detach support : call BOND_RELEASE to detach an enslaved interface.
+ * - give a mini-howto from command-line help : # ifenslave -h
+ *
+ * - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> :
+ * - Master is now brought down before setting the MAC address. In
+ * the 2.4 kernel you can't change the MAC address while the device is
+ * up because you get EBUSY.
+ *
+ * - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com>
+ * - Added the ability to change the active interface on a mode 1 bond
+ * at runtime.
+ *
+ * - 2001/10/23 Chad N. Tindel <ctindel at ieee dot org> :
+ * - No longer set the MAC address of the master. The bond device will
+ * take care of this itself
+ * - Try the SIOC*** versions of the bonding ioctls before using the
+ * old versions
+ * - 2002/02/18 Erik Habbinga <erik_habbinga @ hp dot com> :
+ * - ifr2.ifr_flags was not initialized in the hwaddr_notset case,
+ * SIOCGIFFLAGS now called before hwaddr_notset test
+ *
+ * - 2002/10/31 Tony Cureington <tony.cureington * hp_com> :
+ * - If the master does not have a hardware address when the first slave
+ * is enslaved, the master is assigned the hardware address of that
+ * slave - there is a comment in bonding.c stating "ifenslave takes
+ * care of this now." This corrects the problem of slaves having
+ * different hardware addresses in active-backup mode when
+ * multiple interfaces are specified on a single ifenslave command
+ * (ifenslave bond0 eth0 eth1).
+ *
+ * - 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
+ * Shmulik Hen <shmulik.hen at intel dot com>
+ * - Moved setting the slave's mac address and openning it, from
+ * the application to the driver. This enables support of modes
+ * that need to use the unique mac address of each slave.
+ * The driver also takes care of closing the slave and restoring its
+ * original mac address upon release.
+ * In addition, block possibility of enslaving before the master is up.
+ * This prevents putting the system in an undefined state.
+ *
+ * - 2003/05/01 - Amir Noam <amir.noam at intel dot com>
+ * - Added ABI version control to restore compatibility between
+ * new/old ifenslave and new/old bonding.
+ * - Prevent adding an adapter that is already a slave.
+ * Fixes the problem of stalling the transmission and leaving
+ * the slave in a down state.
+ *
+ * - 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ * - Prevent enslaving if the bond device is down.
+ * Fixes the problem of leaving the system in unstable state and
+ * halting when trying to remove the module.
+ * - Close socket on all abnormal exists.
+ * - Add versioning scheme that follows that of the bonding driver.
+ * current version is 1.0.0 as a base line.
+ *
+ * - 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
+ * - ifenslave -c was broken; it's now fixed
+ * - Fixed problem with routes vanishing from master during enslave
+ * processing.
+ *
+ * - 2003/05/27 - Amir Noam <amir.noam at intel dot com>
+ * - Fix backward compatibility issues:
+ * For drivers not using ABI versions, slave was set down while
+ * it should be left up before enslaving.
+ * Also, master was not set down and the default set_mac_address()
+ * would fail and generate an error message in the system log.
+ * - For opt_c: slave should not be set to the master's setting
+ * while it is running. It was already set during enslave. To
+ * simplify things, it is now handeled separately.
+ *
+ * - 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ * - Code cleanup and style changes
+ * set version to 1.1.0
+ */
+
+#include "libbb.h"
+
+/* #include <net/if.h> - no. linux/if_bonding.h pulls in linux/if.h */
+#include <net/if_arp.h>
+#include <linux/if_bonding.h>
+#include <linux/sockios.h>
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+typedef uint64_t u64; /* hack, so we may include kernel's ethtool.h */
+typedef uint32_t u32; /* ditto */
+typedef uint16_t u16; /* ditto */
+typedef uint8_t u8; /* ditto */
+#include <linux/ethtool.h>
+
+
+struct dev_data {
+ struct ifreq mtu, flags, hwaddr;
+};
+
+
+enum { skfd = 3 }; /* AF_INET socket for ioctl() calls. */
+struct globals {
+ unsigned abi_ver; /* userland - kernel ABI version */
+ smallint hwaddr_set; /* Master's hwaddr is set */
+ struct dev_data master;
+ struct dev_data slave;
+};
+#define G (*ptr_to_globals)
+#define abi_ver (G.abi_ver )
+#define hwaddr_set (G.hwaddr_set)
+#define master (G.master )
+#define slave (G.slave )
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/* NOINLINEs are placed where it results in smaller code (gcc 4.3.1) */
+
+static int ioctl_on_skfd(unsigned request, struct ifreq *ifr)
+{
+ return ioctl(skfd, request, ifr);
+}
+
+static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const char *ifname)
+{
+ strncpy_IFNAMSIZ(ifr->ifr_name, ifname);
+ return ioctl_on_skfd(request, ifr);
+}
+
+static int get_if_settings(char *ifname, struct dev_data *dd)
+{
+ int res;
+
+ res = set_ifrname_and_do_ioctl(SIOCGIFMTU, &dd->mtu, ifname);
+ res |= set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &dd->flags, ifname);
+ res |= set_ifrname_and_do_ioctl(SIOCGIFHWADDR, &dd->hwaddr, ifname);
+
+ return res;
+}
+
+static int get_slave_flags(char *slave_ifname)
+{
+ return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname);
+}
+
+static int set_hwaddr(char *ifname, struct sockaddr *hwaddr)
+{
+ struct ifreq ifr;
+
+ memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr));
+ return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname);
+}
+
+static int set_mtu(char *ifname, int mtu)
+{
+ struct ifreq ifr;
+
+ ifr.ifr_mtu = mtu;
+ return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname);
+}
+
+static int set_if_flags(char *ifname, int flags)
+{
+ struct ifreq ifr;
+
+ ifr.ifr_flags = flags;
+ return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname);
+}
+
+static int set_if_up(char *ifname, int flags)
+{
+ int res = set_if_flags(ifname, flags | IFF_UP);
+ if (res)
+ bb_perror_msg("%s: can't up", ifname);
+ return res;
+}
+
+static int set_if_down(char *ifname, int flags)
+{
+ int res = set_if_flags(ifname, flags & ~IFF_UP);
+ if (res)
+ bb_perror_msg("%s: can't down", ifname);
+ return res;
+}
+
+static int clear_if_addr(char *ifname)
+{
+ struct ifreq ifr;
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data));
+ return set_ifrname_and_do_ioctl(SIOCSIFADDR, &ifr, ifname);
+}
+
+static int set_if_addr(char *master_ifname, char *slave_ifname)
+{
+#if (SIOCGIFADDR | SIOCSIFADDR \
+ | SIOCGIFDSTADDR | SIOCSIFDSTADDR \
+ | SIOCGIFBRDADDR | SIOCSIFBRDADDR \
+ | SIOCGIFNETMASK | SIOCSIFNETMASK) <= 0xffff
+#define INT uint16_t
+#else
+#define INT int
+#endif
+ static const struct {
+ INT g_ioctl;
+ INT s_ioctl;
+ } ifra[] = {
+ { SIOCGIFADDR, SIOCSIFADDR },
+ { SIOCGIFDSTADDR, SIOCSIFDSTADDR },
+ { SIOCGIFBRDADDR, SIOCSIFBRDADDR },
+ { SIOCGIFNETMASK, SIOCSIFNETMASK },
+ };
+
+ struct ifreq ifr;
+ int res;
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(ifra); i++) {
+ res = set_ifrname_and_do_ioctl(ifra[i].g_ioctl, &ifr, master_ifname);
+ if (res < 0) {
+ ifr.ifr_addr.sa_family = AF_INET;
+ memset(ifr.ifr_addr.sa_data, 0,
+ sizeof(ifr.ifr_addr.sa_data));
+ }
+
+ res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname);
+ if (res < 0)
+ return res;
+ }
+
+ return 0;
+}
+
+static void change_active(char *master_ifname, char *slave_ifname)
+{
+ struct ifreq ifr;
+
+ if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
+ bb_error_msg_and_die("%s is not a slave", slave_ifname);
+ }
+
+ strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
+ if (set_ifrname_and_do_ioctl(SIOCBONDCHANGEACTIVE, &ifr, master_ifname)
+ && ioctl_on_skfd(BOND_CHANGE_ACTIVE_OLD, &ifr)
+ ) {
+ bb_perror_msg_and_die(
+ "master %s, slave %s: can't "
+ "change active",
+ master_ifname, slave_ifname);
+ }
+}
+
+static NOINLINE int enslave(char *master_ifname, char *slave_ifname)
+{
+ struct ifreq ifr;
+ int res;
+
+ if (slave.flags.ifr_flags & IFF_SLAVE) {
+ bb_error_msg(
+ "%s is already a slave",
+ slave_ifname);
+ return 1;
+ }
+
+ res = set_if_down(slave_ifname, slave.flags.ifr_flags);
+ if (res)
+ return res;
+
+ if (abi_ver < 2) {
+ /* Older bonding versions would panic if the slave has no IP
+ * address, so get the IP setting from the master.
+ */
+ res = set_if_addr(master_ifname, slave_ifname);
+ if (res) {
+ bb_perror_msg("%s: can't set address", slave_ifname);
+ return res;
+ }
+ } else {
+ res = clear_if_addr(slave_ifname);
+ if (res) {
+ bb_perror_msg("%s: can't clear address", slave_ifname);
+ return res;
+ }
+ }
+
+ if (master.mtu.ifr_mtu != slave.mtu.ifr_mtu) {
+ res = set_mtu(slave_ifname, master.mtu.ifr_mtu);
+ if (res) {
+ bb_perror_msg("%s: can't set MTU", slave_ifname);
+ return res;
+ }
+ }
+
+ if (hwaddr_set) {
+ /* Master already has an hwaddr
+ * so set it's hwaddr to the slave
+ */
+ if (abi_ver < 1) {
+ /* The driver is using an old ABI, so
+ * the application sets the slave's
+ * hwaddr
+ */
+ if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) {
+ bb_perror_msg("%s: can't set hw address",
+ slave_ifname);
+ goto undo_mtu;
+ }
+
+ /* For old ABI the application needs to bring the
+ * slave back up
+ */
+ if (set_if_up(slave_ifname, slave.flags.ifr_flags))
+ goto undo_slave_mac;
+ }
+ /* The driver is using a new ABI,
+ * so the driver takes care of setting
+ * the slave's hwaddr and bringing
+ * it up again
+ */
+ } else {
+ /* No hwaddr for master yet, so
+ * set the slave's hwaddr to it
+ */
+ if (abi_ver < 1) {
+ /* For old ABI, the master needs to be
+ * down before setting it's hwaddr
+ */
+ if (set_if_down(master_ifname, master.flags.ifr_flags))
+ goto undo_mtu;
+ }
+
+ if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) {
+ bb_error_msg("%s: can't set hw address",
+ master_ifname);
+ goto undo_mtu;
+ }
+
+ if (abi_ver < 1) {
+ /* For old ABI, bring the master
+ * back up
+ */
+ if (set_if_up(master_ifname, master.flags.ifr_flags))
+ goto undo_master_mac;
+ }
+
+ hwaddr_set = 1;
+ }
+
+ /* Do the real thing */
+ strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
+ if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname)
+ && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr)
+ ) {
+ goto undo_master_mac;
+ }
+
+ return 0;
+
+/* rollback (best effort) */
+ undo_master_mac:
+ set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr));
+ hwaddr_set = 0;
+ goto undo_mtu;
+
+ undo_slave_mac:
+ set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr));
+ undo_mtu:
+ set_mtu(slave_ifname, slave.mtu.ifr_mtu);
+ return 1;
+}
+
+static int release(char *master_ifname, char *slave_ifname)
+{
+ struct ifreq ifr;
+ int res = 0;
+
+ if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
+ bb_error_msg("%s is not a slave", slave_ifname);
+ return 1;
+ }
+
+ strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
+ if (set_ifrname_and_do_ioctl(SIOCBONDRELEASE, &ifr, master_ifname) < 0
+ && ioctl_on_skfd(BOND_RELEASE_OLD, &ifr) < 0
+ ) {
+ return 1;
+ }
+ if (abi_ver < 1) {
+ /* The driver is using an old ABI, so we'll set the interface
+ * down to avoid any conflicts due to same MAC/IP
+ */
+ res = set_if_down(slave_ifname, slave.flags.ifr_flags);
+ }
+
+ /* set to default mtu */
+ set_mtu(slave_ifname, 1500);
+
+ return res;
+}
+
+static NOINLINE void get_drv_info(char *master_ifname)
+{
+ struct ifreq ifr;
+ struct ethtool_drvinfo info;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_data = (caddr_t)&info;
+ info.cmd = ETHTOOL_GDRVINFO;
+ /* both fields are 32 bytes long (long enough) */
+ strcpy(info.driver, "ifenslave");
+ strcpy(info.fw_version, utoa(BOND_ABI_VERSION));
+ if (set_ifrname_and_do_ioctl(SIOCETHTOOL, &ifr, master_ifname) < 0) {
+ if (errno == EOPNOTSUPP)
+ return;
+ bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
+ }
+
+ abi_ver = bb_strtou(info.fw_version, NULL, 0);
+ if (errno)
+ bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
+}
+
+int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ifenslave_main(int argc UNUSED_PARAM, char **argv)
+{
+ char *master_ifname, *slave_ifname;
+ int rv;
+ int res;
+ unsigned opt;
+ enum {
+ OPT_c = (1 << 0),
+ OPT_d = (1 << 1),
+ OPT_f = (1 << 2),
+ };
+#if ENABLE_GETOPT_LONG
+ static const char ifenslave_longopts[] ALIGN1 =
+ "change-active\0" No_argument "c"
+ "detach\0" No_argument "d"
+ "force\0" No_argument "f"
+ /* "all-interfaces\0" No_argument "a" */
+ ;
+
+ applet_long_options = ifenslave_longopts;
+#endif
+ INIT_G();
+
+ opt = getopt32(argv, "cdfa");
+ argv += optind;
+ if (opt & (opt-1)) /* Only one option can be given */
+ bb_show_usage();
+
+ master_ifname = *argv++;
+
+ /* No interface names - show all interfaces. */
+ if (!master_ifname) {
+ display_interfaces(NULL);
+ return EXIT_SUCCESS;
+ }
+
+ /* Open a basic socket */
+ xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd);
+
+ /* Exchange abi version with bonding module */
+ get_drv_info(master_ifname);
+
+ slave_ifname = *argv++;
+ if (!slave_ifname) {
+ if (opt & (OPT_d|OPT_c)) {
+ /* --change or --detach, and no slaves given -
+ * show all interfaces. */
+ display_interfaces(slave_ifname /* == NULL */);
+ return 2; /* why 2? */
+ }
+ /* A single arg means show the
+ * configuration for this interface
+ */
+ display_interfaces(master_ifname);
+ return EXIT_SUCCESS;
+ }
+
+ if (get_if_settings(master_ifname, &master)) {
+ /* Probably a good reason not to go on */
+ bb_perror_msg_and_die("%s: can't get settings", master_ifname);
+ }
+
+ /* Check if master is indeed a master;
+ * if not then fail any operation
+ */
+ if (!(master.flags.ifr_flags & IFF_MASTER))
+ bb_error_msg_and_die("%s is not a master", master_ifname);
+
+ /* Check if master is up; if not then fail any operation */
+ if (!(master.flags.ifr_flags & IFF_UP))
+ bb_error_msg_and_die("%s is not up", master_ifname);
+
+#ifdef WHY_BOTHER
+ /* Neither -c[hange] nor -d[etach] -> it's "enslave" then;
+ * and -f[orce] is not there too. Check that it's ethernet. */
+ if (!(opt & (OPT_d|OPT_c|OPT_f)) {
+ /* The family '1' is ARPHRD_ETHER for ethernet. */
+ if (master.hwaddr.ifr_hwaddr.sa_family != 1) {
+ bb_error_msg_and_die(
+ "%s is not ethernet-like (-f overrides)",
+ master_ifname);
+ }
+ }
+#endif
+
+ /* Accepts only one slave */
+ if (opt & OPT_c) {
+ /* Change active slave */
+ if (get_slave_flags(slave_ifname)) {
+ bb_perror_msg_and_die(
+ "%s: can't get flags", slave_ifname);
+ }
+ change_active(master_ifname, slave_ifname);
+ return EXIT_SUCCESS;
+ }
+
+ /* Accepts multiple slaves */
+ res = 0;
+ do {
+ if (opt & OPT_d) {
+ /* Detach a slave interface from the master */
+ rv = get_slave_flags(slave_ifname);
+ if (rv) {
+ /* Can't work with this slave, */
+ /* remember the error and skip it */
+ bb_perror_msg(
+ "skipping %s: can't get flags",
+ slave_ifname);
+ res = rv;
+ continue;
+ }
+ rv = release(master_ifname, slave_ifname);
+ if (rv) {
+ bb_perror_msg("can't release %s from %s",
+ slave_ifname, master_ifname);
+ res = rv;
+ }
+ } else {
+ /* Attach a slave interface to the master */
+ rv = get_if_settings(slave_ifname, &slave);
+ if (rv) {
+ /* Can't work with this slave, */
+ /* remember the error and skip it */
+ bb_perror_msg(
+ "skipping %s: can't get settings",
+ slave_ifname);
+ res = rv;
+ continue;
+ }
+ rv = enslave(master_ifname, slave_ifname);
+ if (rv) {
+ bb_perror_msg("can't enslave %s to %s",
+ slave_ifname, master_ifname);
+ res = rv;
+ }
+ }
+ } while ((slave_ifname = *argv++) != NULL);
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ close(skfd);
+ }
+
+ return res;
+}
diff --git a/release/src/router/busybox/networking/ifupdown.c b/release/src/router/busybox/networking/ifupdown.c
index bae9f4ea..dc7ed490 100644
--- a/release/src/router/busybox/networking/ifupdown.c
+++ b/release/src/router/busybox/networking/ifupdown.c
@@ -1,8 +1,8 @@
/* vi: set sw=4 ts=4: */
/*
* ifupdown for busybox
- * Copyright (c) 2002 Glenn McGrath <bug1@optushome.com.au>
- * Copyright (c) 2003 Erik Andersen <andersen@codepoet.org>
+ * Copyright (c) 2002 Glenn McGrath
+ * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
*
* Based on ifupdown v 0.6.4 by Anthony Towns
* Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
@@ -10,37 +10,15 @@
* Changes to upstream version
* Remove checks for kernel version, assume kernel version 2.2.0 or better.
* Lines in the interfaces file cannot wrap.
- * To adhere to the FHS, the default state file is /var/run/ifstate.
+ * To adhere to the FHS, the default state file is /var/run/ifstate
+ * (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
+ * configuration.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-#include <sys/stat.h>
#include <sys/utsname.h>
-#include <sys/wait.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
#include <fnmatch.h>
-#include <getopt.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
#include "libbb.h"
@@ -49,61 +27,32 @@
#define EUNDEFVAR 10002
#define EUNBALPER 10000
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
#define MAX_INTERFACE_LENGTH 10
#endif
-#if 0
-#define debug_noise(fmt, args...) printf(fmt, ## args)
-#else
-#define debug_noise(fmt, args...)
-#endif
+#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
+
+#define debug_noise(args...) /*fprintf(stderr, args)*/
/* Forward declaration */
struct interface_defn_t;
-typedef int (execfn)(char *command);
-typedef int (command_set)(struct interface_defn_t *ifd, execfn *e);
-
-extern llist_t *llist_add_to_end(llist_t *list_head, char *data)
-{
- llist_t *new_item, *tmp, *prev;
-
- new_item = xmalloc(sizeof(llist_t));
- new_item->data = data;
- new_item->link = NULL;
-
- prev = NULL;
- tmp = list_head;
- while(tmp) {
- prev = tmp;
- tmp = tmp->link;
- }
- if (prev) {
- prev->link = new_item;
- } else {
- list_head = new_item;
- }
-
- return(list_head);
-}
+typedef int execfn(char *command);
-struct method_t
-{
- char *name;
- command_set *up;
- command_set *down;
+struct method_t {
+ const char *name;
+ int (*up)(struct interface_defn_t *ifd, execfn *e);
+ int (*down)(struct interface_defn_t *ifd, execfn *e);
};
-struct address_family_t
-{
- char *name;
+struct address_family_t {
+ const char *name;
int n_methods;
- struct method_t *method;
+ const struct method_t *method;
};
-struct mapping_defn_t
-{
+struct mapping_defn_t {
struct mapping_defn_t *next;
int max_matches;
@@ -117,541 +66,609 @@ struct mapping_defn_t
char **mapping;
};
-struct variable_t
-{
+struct variable_t {
char *name;
char *value;
};
-struct interface_defn_t
-{
- struct interface_defn_t *prev;
- struct interface_defn_t *next;
+struct interface_defn_t {
+ const struct address_family_t *address_family;
+ const struct method_t *method;
char *iface;
- struct address_family_t *address_family;
- struct method_t *method;
-
- int automatic;
-
int max_options;
int n_options;
struct variable_t *option;
};
-struct interfaces_file_t
-{
+struct interfaces_file_t {
llist_t *autointerfaces;
llist_t *ifaces;
struct mapping_defn_t *mappings;
};
-static char no_act = 0;
-static char verbose = 0;
-static char **environ = NULL;
+#define OPTION_STR "anvf" USE_FEATURE_IFUPDOWN_MAPPING("m") "i:"
+enum {
+ OPT_do_all = 0x1,
+ OPT_no_act = 0x2,
+ OPT_verbose = 0x4,
+ OPT_force = 0x8,
+ OPT_no_mappings = 0x10,
+};
+#define DO_ALL (option_mask32 & OPT_do_all)
+#define NO_ACT (option_mask32 & OPT_no_act)
+#define VERBOSE (option_mask32 & OPT_verbose)
+#define FORCE (option_mask32 & OPT_force)
+#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+static char **my_environ;
-static unsigned int count_bits(unsigned int a)
-{
- unsigned int result;
- result = (a & 0x55) + ((a >> 1) & 0x55);
- result = (result & 0x33) + ((result >> 2) & 0x33);
- return((result & 0x0F) + ((result >> 4) & 0x0F));
-}
+static const char *startup_PATH;
-static int count_netmask_bits(char *dotted_quad)
-{
- unsigned int result, a, b, c, d;
- /* Found a netmask... Check if it is dotted quad */
- if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
- return -1;
- result = count_bits(a);
- result += count_bits(b);
- result += count_bits(c);
- result += count_bits(d);
- return ((int)result);
-}
-#endif
+#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
-static void addstr(char **buf, size_t *len, size_t *pos, char *str, size_t str_length)
+static void addstr(char **bufp, const char *str, size_t str_length)
{
- if (*pos + str_length >= *len) {
- char *newbuf;
-
- newbuf = xrealloc(*buf, *len * 2 + str_length + 1);
- *buf = newbuf;
- *len = *len * 2 + str_length + 1;
- }
-
- while (str_length-- >= 1) {
- (*buf)[(*pos)++] = *str;
- str++;
- }
- (*buf)[*pos] = '\0';
+ /* xasprintf trick will be smaller, but we are often
+ * called with str_length == 1 - don't want to have
+ * THAT much of malloc/freeing! */
+ char *buf = *bufp;
+ int len = (buf ? strlen(buf) : 0);
+ str_length++;
+ buf = xrealloc(buf, len + str_length);
+ /* copies at most str_length-1 chars! */
+ safe_strncpy(buf + len, str, str_length);
+ *bufp = buf;
}
-static int strncmpz(char *l, char *r, size_t llen)
+static int strncmpz(const char *l, const char *r, size_t llen)
{
int i = strncmp(l, r, llen);
- if (i == 0) {
- return(-r[llen]);
- } else {
- return(i);
- }
+ if (i == 0)
+ return -r[llen];
+ return i;
}
-static char *get_var(char *id, size_t idlen, struct interface_defn_t *ifd)
+static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
{
int i;
if (strncmpz(id, "iface", idlen) == 0) {
- char *result;
- static char label_buf[20];
- strncpy(label_buf, ifd->iface, 19);
- label_buf[19]=0;
- result = strchr(label_buf, ':');
- if (result) {
- *result=0;
- }
- return( label_buf);
- } else if (strncmpz(id, "label", idlen) == 0) {
- return (ifd->iface);
- } else {
- for (i = 0; i < ifd->n_options; i++) {
- if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
- return (ifd->option[i].value);
- }
+ static char *label_buf;
+ //char *result;
+
+ free(label_buf);
+ label_buf = xstrdup(ifd->iface);
+ // Remove virtual iface suffix - why?
+ // ubuntu's ifup doesn't do this
+ //result = strchrnul(label_buf, ':');
+ //*result = '\0';
+ return label_buf;
+ }
+ if (strncmpz(id, "label", idlen) == 0) {
+ return ifd->iface;
+ }
+ for (i = 0; i < ifd->n_options; i++) {
+ if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
+ return ifd->option[i].value;
}
}
-
- return(NULL);
+ return NULL;
}
-static char *parse(char *command, struct interface_defn_t *ifd)
+#if ENABLE_FEATURE_IFUPDOWN_IP
+static int count_netmask_bits(const char *dotted_quad)
{
+// int result;
+// unsigned a, b, c, d;
+// /* Found a netmask... Check if it is dotted quad */
+// if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+// return -1;
+// if ((a|b|c|d) >> 8)
+// return -1; /* one of numbers is >= 256 */
+// d |= (a << 24) | (b << 16) | (c << 8); /* IP */
+// d = ~d; /* 11110000 -> 00001111 */
+
+ /* Shorter version */
+ int result;
+ struct in_addr ip;
+ unsigned d;
+
+ if (inet_aton(dotted_quad, &ip) == 0)
+ return -1; /* malformed dotted IP */
+ d = ntohl(ip.s_addr); /* IP in host order */
+ d = ~d; /* 11110000 -> 00001111 */
+ if (d & (d+1)) /* check that it is in 00001111 form */
+ return -1; /* no it is not */
+ result = 32;
+ while (d) {
+ d >>= 1;
+ result--;
+ }
+ return result;
+}
+#endif
- char *result = NULL;
- size_t pos = 0, len = 0;
+static char *parse(const char *command, struct interface_defn_t *ifd)
+{
size_t old_pos[MAX_OPT_DEPTH] = { 0 };
int okay[MAX_OPT_DEPTH] = { 1 };
int opt_depth = 1;
+ char *result = NULL;
while (*command) {
switch (*command) {
-
- default:
- addstr(&result, &len, &pos, command, 1);
+ default:
+ addstr(&result, command, 1);
+ command++;
+ break;
+ case '\\':
+ if (command[1]) {
+ addstr(&result, command + 1, 1);
+ command += 2;
+ } else {
+ addstr(&result, command, 1);
command++;
- break;
- case '\\':
- if (command[1]) {
- addstr(&result, &len, &pos, command + 1, 1);
- command += 2;
- } else {
- addstr(&result, &len, &pos, command, 1);
- command++;
+ }
+ break;
+ case '[':
+ if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
+ old_pos[opt_depth] = result ? strlen(result) : 0;
+ okay[opt_depth] = 1;
+ opt_depth++;
+ command += 2;
+ } else {
+ addstr(&result, "[", 1);
+ command++;
+ }
+ break;
+ case ']':
+ if (command[1] == ']' && opt_depth > 1) {
+ opt_depth--;
+ if (!okay[opt_depth]) {
+ result[old_pos[opt_depth]] = '\0';
}
- break;
- case '[':
- if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
- old_pos[opt_depth] = pos;
- okay[opt_depth] = 1;
- opt_depth++;
- command += 2;
- } else {
- addstr(&result, &len, &pos, "[", 1);
- command++;
+ command += 2;
+ } else {
+ addstr(&result, "]", 1);
+ command++;
+ }
+ break;
+ case '%':
+ {
+ char *nextpercent;
+ char *varvalue;
+
+ command++;
+ nextpercent = strchr(command, '%');
+ if (!nextpercent) {
+ errno = EUNBALPER;
+ free(result);
+ return NULL;
}
- break;
- case ']':
- if (command[1] == ']' && opt_depth > 1) {
- opt_depth--;
- if (!okay[opt_depth]) {
- pos = old_pos[opt_depth];
- result[pos] = '\0';
+
+ varvalue = get_var(command, nextpercent - command, ifd);
+
+ if (varvalue) {
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* "hwaddress <class> <address>":
+ * unlike ifconfig, ip doesnt want <class>
+ * (usually "ether" keyword). Skip it. */
+ if (strncmp(command, "hwaddress", 9) == 0) {
+ varvalue = skip_whitespace(skip_non_whitespace(varvalue));
}
- command += 2;
+#endif
+ addstr(&result, varvalue, strlen(varvalue));
} else {
- addstr(&result, &len, &pos, "]", 1);
- command++;
- }
- break;
- case '%':
- {
- char *nextpercent;
- char *varvalue;
-
- command++;
- nextpercent = strchr(command, '%');
- if (!nextpercent) {
- errno = EUNBALPER;
- free(result);
- return (NULL);
- }
-
- varvalue = get_var(command, nextpercent - command, ifd);
-
- if (varvalue) {
- addstr(&result, &len, &pos, varvalue, bb_strlen(varvalue));
- } else {
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
- /* Sigh... Add a special case for 'ip' to convert from
- * dotted quad to bit count style netmasks. */
- if (strncmp(command, "bnmask", 6)==0) {
- int res;
- varvalue = get_var("netmask", 7, ifd);
- if (varvalue && (res=count_netmask_bits(varvalue)) > 0) {
- char argument[255];
- sprintf(argument, "%d", res);
- addstr(&result, &len, &pos, argument, bb_strlen(argument));
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* Sigh... Add a special case for 'ip' to convert from
+ * dotted quad to bit count style netmasks. */
+ if (strncmp(command, "bnmask", 6) == 0) {
+ unsigned res;
+ varvalue = get_var("netmask", 7, ifd);
+ if (varvalue) {
+ res = count_netmask_bits(varvalue);
+ if (res > 0) {
+ const char *argument = utoa(res);
+ addstr(&result, argument, strlen(argument));
command = nextpercent + 1;
break;
}
}
-#endif
- okay[opt_depth - 1] = 0;
}
-
- command = nextpercent + 1;
+#endif
+ okay[opt_depth - 1] = 0;
}
- break;
+
+ command = nextpercent + 1;
+ }
+ break;
}
}
if (opt_depth > 1) {
errno = EUNBALBRACK;
free(result);
- return(NULL);
+ return NULL;
}
if (!okay[0]) {
errno = EUNDEFVAR;
free(result);
- return(NULL);
+ return NULL;
}
- return(result);
+ return result;
}
-static int execute(char *command, struct interface_defn_t *ifd, execfn *exec)
+/* execute() returns 1 for success and 0 for failure */
+static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
{
char *out;
int ret;
out = parse(command, ifd);
if (!out) {
- return(0);
+ /* parse error? */
+ return 0;
}
- ret = (*exec) (out);
+ /* out == "": parsed ok but not all needed variables known, skip */
+ ret = out[0] ? (*exec)(out) : 1;
free(out);
- return(1);
-}
-
-#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
-static int static_up_ipx(struct interface_defn_t *ifd, execfn *exec)
-{
- return(execute("ipx_interface add %iface% %frame% %netnum%", ifd, exec));
-}
-
-static int static_down_ipx(struct interface_defn_t *ifd, execfn *exec)
-{
- return(execute("ipx_interface del %iface% %frame%", ifd, exec));
-}
-
-static int dynamic_up(struct interface_defn_t *ifd, execfn *exec)
-{
- return(execute("ipx_interface add %iface% %frame%", ifd, exec));
-}
-
-static int dynamic_down(struct interface_defn_t *ifd, execfn *exec)
-{
- return(execute("ipx_interface del %iface% %frame%", ifd, exec));
+ if (ret != 1) {
+ return 0;
+ }
+ return 1;
}
+#endif
-static struct method_t methods_ipx[] = {
- { "dynamic", dynamic_up, dynamic_down, },
- { "static", static_up_ipx, static_down_ipx, },
-};
-
-struct address_family_t addr_ipx = {
- "ipx",
- sizeof(methods_ipx) / sizeof(struct method_t),
- methods_ipx
-};
-#endif /* IFUP_FEATURE_IPX */
-
-#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
static int loopback_up6(struct interface_defn_t *ifd, execfn *exec)
{
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+#if ENABLE_FEATURE_IFUPDOWN_IP
int result;
- result =execute("ip addr add ::1 dev %iface% label %label%", ifd, exec);
+ result = execute("ip addr add ::1 dev %iface%", ifd, exec);
result += execute("ip link set %iface% up", ifd, exec);
- return( result);
+ return ((result == 2) ? 2 : 0);
#else
- return( execute("ifconfig %iface% add ::1", ifd, exec));
+ return execute("ifconfig %iface% add ::1", ifd, exec);
#endif
}
static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)
{
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
- return(execute("ip link set %iface% down", ifd, exec));
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ return execute("ip link set %iface% down", ifd, exec);
#else
- return(execute("ifconfig %iface% del ::1", ifd, exec));
+ return execute("ifconfig %iface% del ::1", ifd, exec);
#endif
}
static int static_up6(struct interface_defn_t *ifd, execfn *exec)
{
int result;
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
- result = execute("ip addr add %address%/%netmask% dev %iface% label %label%", ifd, exec);
- result += execute("ip link set %iface% up", ifd, exec);
- result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
+ result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
+ /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
+ result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
#else
- result = execute("ifconfig %iface% [[media %media%]] [[hw %hwaddress%]] [[mtu %mtu%]] up", ifd, exec);
+ result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
- result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec);
+ result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec);
#endif
- return( result);
+ return ((result == 3) ? 3 : 0);
}
static int static_down6(struct interface_defn_t *ifd, execfn *exec)
{
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
- return(execute("ip link set %iface% down", ifd, exec));
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ return execute("ip link set %iface% down", ifd, exec);
#else
- return(execute("ifconfig %iface% down", ifd, exec));
+ return execute("ifconfig %iface% down", ifd, exec);
#endif
}
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+#if ENABLE_FEATURE_IFUPDOWN_IP
static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
{
int result;
result = execute("ip tunnel add %iface% mode sit remote "
- "%endpoint% [[local %local%]] [[ttl %ttl%]]", ifd, exec);
+ "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
result += execute("ip link set %iface% up", ifd, exec);
- result += execute("ip addr add %address%/%netmask% dev %iface% label %label%", ifd, exec);
- result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
- return( result);
+ result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
+ result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
+ return ((result == 4) ? 4 : 0);
}
static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
{
- return( execute("ip tunnel del %iface%", ifd, exec));
+ return execute("ip tunnel del %iface%", ifd, exec);
}
#endif
-static struct method_t methods6[] = {
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+static const struct method_t methods6[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IP
{ "v4tunnel", v4tunnel_up, v4tunnel_down, },
#endif
{ "static", static_up6, static_down6, },
{ "loopback", loopback_up6, loopback_down6, },
};
-struct address_family_t addr_inet6 = {
+static const struct address_family_t addr_inet6 = {
"inet6",
- sizeof(methods6) / sizeof(struct method_t),
+ ARRAY_SIZE(methods6),
methods6
};
-#endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */
+#endif /* FEATURE_IFUPDOWN_IPV6 */
-#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
static int loopback_up(struct interface_defn_t *ifd, execfn *exec)
{
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+#if ENABLE_FEATURE_IFUPDOWN_IP
int result;
- result = execute("ip addr add 127.0.0.1/8 dev %iface% label %label%", ifd, exec);
+ result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
result += execute("ip link set %iface% up", ifd, exec);
- return(result);
+ return ((result == 2) ? 2 : 0);
#else
- return( execute("ifconfig %iface% 127.0.0.1 up", ifd, exec));
+ return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
#endif
}
static int loopback_down(struct interface_defn_t *ifd, execfn *exec)
{
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+#if ENABLE_FEATURE_IFUPDOWN_IP
int result;
result = execute("ip addr flush dev %iface%", ifd, exec);
result += execute("ip link set %iface% down", ifd, exec);
- return(result);
+ return ((result == 2) ? 2 : 0);
#else
- return( execute("ifconfig %iface% 127.0.0.1 down", ifd, exec));
+ return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
#endif
}
static int static_up(struct interface_defn_t *ifd, execfn *exec)
{
int result;
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
- result = execute("ip addr add %address%/%bnmask% [[broadcast %broadcast%]] "
- "dev %iface% label %label%", ifd, exec);
- result += execute("ip link set %iface% up", ifd, exec);
- result += execute("[[ ip route add default via %gateway% dev %iface% ]]", ifd, exec);
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
+ "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
+ result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
+ result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec);
+ return ((result == 3) ? 3 : 0);
#else
- result = execute("ifconfig %iface% %address% netmask %netmask% "
- "[[broadcast %broadcast%]] [[pointopoint %pointopoint%]] "
- "[[media %media%]] [[mtu %mtu%]] [[hw %hwaddress%]] up",
+ /* ifconfig said to set iface up before it processes hw %hwaddress%,
+ * which then of course fails. Thus we run two separate ifconfig */
+ result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
ifd, exec);
- result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec);
+ result += execute("ifconfig %iface% %address% netmask %netmask%"
+ "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
+ ifd, exec);
+ result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+ return ((result == 3) ? 3 : 0);
#endif
- return(result);
}
static int static_down(struct interface_defn_t *ifd, execfn *exec)
{
int result;
-#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+#if ENABLE_FEATURE_IFUPDOWN_IP
result = execute("ip addr flush dev %iface%", ifd, exec);
result += execute("ip link set %iface% down", ifd, exec);
#else
- result = execute("[[ route del default gw %gateway% %iface% ]]", ifd, exec);
+ /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
+ /* Bringing the interface down deletes the routes in itself.
+ Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
+ result = 1;
result += execute("ifconfig %iface% down", ifd, exec);
#endif
- return(result);
+ return ((result == 2) ? 2 : 0);
}
-static int execable(char *program)
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+struct dhcp_client_t
{
- struct stat buf;
- if (0 == stat(program, &buf)) {
- if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode)) {
- return(1);
- }
- }
- return(0);
-}
+ const char *name;
+ const char *startcmd;
+ const char *stopcmd;
+};
+static const struct dhcp_client_t ext_dhcp_clients[] = {
+ { "dhcpcd",
+ "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
+ "dhcpcd -k %iface%",
+ },
+ { "dhclient",
+ "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
+ "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
+ },
+ { "pump",
+ "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
+ "pump -i %iface% -k",
+ },
+ { "udhcpc",
+ "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]]"
+ "[[ -s %script%]][[ %udhcpc_opts%]]",
+ "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
+ },
+};
+#endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
+
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
{
- if (execable("/sbin/udhcpc")) {
- return( execute("udhcpc -n -p /var/run/udhcpc.%iface%.pid -i "
- "%iface% [[-H %hostname%]] [[-c %clientid%]]", ifd, exec));
- } else if (execable("/sbin/pump")) {
- return( execute("pump -i %iface% [[-h %hostname%]] [[-l %leasehours%]]", ifd, exec));
- } else if (execable("/sbin/dhclient")) {
- return( execute("dhclient -pf /var/run/dhclient.%iface%.pid %iface%", ifd, exec));
- } else if (execable("/sbin/dhcpcd")) {
- return( execute("dhcpcd [[-h %hostname%]] [[-i %vendor%]] [[-I %clientid%]] "
- "[[-l %leasetime%]] %iface%", ifd, exec));
+ unsigned i;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* ip doesn't up iface when it configures it (unlike ifconfig) */
+ if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
+ return 0;
+#else
+ /* needed if we have hwaddress on dhcp iface */
+ if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
+ return 0;
+#endif
+ for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
+ if (exists_execable(ext_dhcp_clients[i].name))
+ return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
}
- return(0);
+ bb_error_msg("no dhcp clients found");
+ return 0;
+}
+#elif ENABLE_APP_UDHCPC
+static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* ip doesn't up iface when it configures it (unlike ifconfig) */
+ if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
+ return 0;
+#else
+ /* needed if we have hwaddress on dhcp iface */
+ if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
+ return 0;
+#endif
+ return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
+ "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]][[ %udhcpc_opts%]]",
+ ifd, exec);
}
+#else
+static int dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM,
+ execfn *exec UNUSED_PARAM)
+{
+ return 0; /* no dhcp support */
+}
+#endif
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
{
int result = 0;
- if (execable("/sbin/udhcpc")) {
- execute("kill -9 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
- } else if (execable("/sbin/pump")) {
- result = execute("pump -i %iface% -k", ifd, exec);
- } else if (execable("/sbin/dhclient")) {
- execute("kill -9 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
- } else if (execable("/sbin/dhcpcd")) {
- result = execute("dhcpcd -k %iface%", ifd, exec);
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
+ if (exists_execable(ext_dhcp_clients[i].name)) {
+ result += execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
+ if (result)
+ break;
+ }
}
- return (result || execute("ifconfig %iface% down", ifd, exec));
+
+ if (!result)
+ bb_error_msg("warning: no dhcp clients found and stopped");
+
+ /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
+ and it may come back up because udhcpc is still shutting down */
+ usleep(100000);
+ result += static_down(ifd, exec);
+ return ((result == 3) ? 3 : 0);
}
+#elif ENABLE_APP_UDHCPC
+static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+ result = execute("kill "
+ "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+ /* Also bring the hardware interface down since
+ killing the dhcp client alone doesn't do it.
+ This enables consecutive ifup->ifdown->ifup */
+ /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
+ and it may come back up because udhcpc is still shutting down */
+ usleep(100000);
+ result += static_down(ifd, exec);
+ return ((result == 3) ? 3 : 0);
+}
+#else
+static int dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM,
+ execfn *exec UNUSED_PARAM)
+{
+ return 0; /* no dhcp support */
+}
+#endif
-static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
+static int manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
{
- return( execute("bootpc [[--bootfile %bootfile%]] --dev %iface% "
- "[[--server %server%]] [[--hwaddr %hwaddr%]] "
- "--returniffail --serverbcast", ifd, exec));
+ return 1;
}
-static int bootp_down(struct interface_defn_t *ifd, execfn *exec)
+static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
{
- return( execute("ifconfig down %iface%", ifd, exec));
+ return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
+ "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
+ " --returniffail --serverbcast", ifd, exec);
}
static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
{
- return( execute("pon [[%provider%]]", ifd, exec));
+ return execute("pon[[ %provider%]]", ifd, exec);
}
static int ppp_down(struct interface_defn_t *ifd, execfn *exec)
{
- return( execute("poff [[%provider%]]", ifd, exec));
+ return execute("poff[[ %provider%]]", ifd, exec);
}
static int wvdial_up(struct interface_defn_t *ifd, execfn *exec)
{
- return( execute("/sbin/start-stop-daemon --start -x /usr/bin/wvdial "
- "-p /var/run/wvdial.%iface% -b -m -- [[ %provider% ]]", ifd, exec));
+ return execute("start-stop-daemon --start -x wvdial "
+ "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
}
static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)
{
- return( execute("/sbin/start-stop-daemon --stop -x /usr/bin/wvdial "
- "-p /var/run/wvdial.%iface% -s 2", ifd, exec));
+ return execute("start-stop-daemon --stop -x wvdial "
+ "-p /var/run/wvdial.%iface% -s 2", ifd, exec);
}
-static struct method_t methods[] =
-{
+static const struct method_t methods[] = {
+ { "manual", manual_up_down, manual_up_down, },
{ "wvdial", wvdial_up, wvdial_down, },
{ "ppp", ppp_up, ppp_down, },
{ "static", static_up, static_down, },
- { "bootp", bootp_up, bootp_down, },
+ { "bootp", bootp_up, static_down, },
{ "dhcp", dhcp_up, dhcp_down, },
{ "loopback", loopback_up, loopback_down, },
};
-struct address_family_t addr_inet =
-{
+static const struct address_family_t addr_inet = {
"inet",
- sizeof(methods) / sizeof(struct method_t),
+ ARRAY_SIZE(methods),
methods
};
-#endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */
+#endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */
static char *next_word(char **buf)
{
- unsigned short length;
+ unsigned length;
char *word;
- if ((buf == NULL) || (*buf == NULL) || (**buf == '\0')) {
- return NULL;
- }
-
/* Skip over leading whitespace */
- word = *buf;
- while (isspace(*word)) {
- ++word;
- }
+ word = skip_whitespace(*buf);
- /* Skip over comments */
- if (*word == '#') {
- return(NULL);
- }
+ /* Stop on EOL */
+ if (*word == '\0')
+ return NULL;
- /* Find the length of this word */
+ /* Find the length of this word (can't be 0) */
length = strcspn(word, " \t\n");
- if (length == 0) {
- return(NULL);
- }
+
+ /* Unless we are already at NUL, store NUL and advance */
+ if (word[length] != '\0')
+ word[length++] = '\0';
+
*buf = word + length;
- /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */
- if (**buf) {
- **buf = '\0';
- (*buf)++;
- }
return word;
}
-static struct address_family_t *get_address_family(struct address_family_t *af[], char *name)
+static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
{
int i;
+ if (!name)
+ return NULL;
+
for (i = 0; af[i]; i++) {
if (strcmp(af[i]->name, name) == 0) {
return af[i];
@@ -660,278 +677,230 @@ static struct address_family_t *get_address_family(struct address_family_t *af[]
return NULL;
}
-static struct method_t *get_method(struct address_family_t *af, char *name)
+static const struct method_t *get_method(const struct address_family_t *af, char *name)
{
int i;
+ if (!name)
+ return NULL;
+ /* TODO: use index_in_str_array() */
for (i = 0; i < af->n_methods; i++) {
if (strcmp(af->method[i].name, name) == 0) {
return &af->method[i];
}
}
- return(NULL);
-}
-
-static int duplicate_if(struct interface_defn_t *ifa, struct interface_defn_t *ifb)
-{
- if (strcmp(ifa->iface, ifb->iface) != 0) {
- return(0);
- }
- if (ifa->address_family != ifb->address_family) {
- return(0);
- }
- return(1);
-}
-
-static const llist_t *find_list_string(const llist_t *list, const char *string)
-{
- while (list) {
- if (strcmp(list->data, string) == 0) {
- return(list);
- }
- list = list->link;
- }
- return(NULL);
+ return NULL;
}
-static struct interfaces_file_t *read_interfaces(char *filename)
+static struct interfaces_file_t *read_interfaces(const char *filename)
{
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ /* Let's try to be compatible.
+ *
+ * "man 5 interfaces" says:
+ * Lines starting with "#" are ignored. Note that end-of-line
+ * comments are NOT supported, comments must be on a line of their own.
+ * A line may be extended across multiple lines by making
+ * the last character a backslash.
+ *
+ * Seen elsewhere in example config file:
+ * A first non-blank "#" character makes the rest of the line
+ * be ignored. Blank lines are ignored. Lines may be indented freely.
+ * A "\" character at the very end of the line indicates the next line
+ * should be treated as a continuation of the current one.
+ */
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
struct mapping_defn_t *currmap = NULL;
#endif
struct interface_defn_t *currif = NULL;
struct interfaces_file_t *defn;
FILE *f;
- char *firstword;
char *buf;
-
+ char *first_word;
+ char *rest_of_line;
enum { NONE, IFACE, MAPPING } currently_processing = NONE;
- defn = xmalloc(sizeof(struct interfaces_file_t));
- defn->autointerfaces = NULL;
- defn->mappings = NULL;
- defn->ifaces = NULL;
-
- f = bb_xfopen(filename, "r");
-
- while ((buf = bb_get_chomped_line_from_file(f)) != NULL) {
- char *buf_ptr = buf;
-
- firstword = next_word(&buf_ptr);
- if (firstword == NULL) {
+ defn = xzalloc(sizeof(*defn));
+ f = xfopen_for_read(filename);
+
+ while ((buf = xmalloc_fgetline(f)) != NULL) {
+#if ENABLE_DESKTOP
+ /* Trailing "\" concatenates lines */
+ char *p;
+ while ((p = last_char_is(buf, '\\')) != NULL) {
+ *p = '\0';
+ rest_of_line = xmalloc_fgetline(f);
+ if (!rest_of_line)
+ break;
+ p = xasprintf("%s%s", buf, rest_of_line);
+ free(buf);
+ free(rest_of_line);
+ buf = p;
+ }
+#endif
+ rest_of_line = buf;
+ first_word = next_word(&rest_of_line);
+ if (!first_word || *first_word == '#') {
free(buf);
- continue; /* blank line */
+ continue; /* blank/comment line */
}
- if (strcmp(firstword, "mapping") == 0) {
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
- currmap = xmalloc(sizeof(struct mapping_defn_t));
- currmap->max_matches = 0;
- currmap->n_matches = 0;
- currmap->match = NULL;
-
- while ((firstword = next_word(&buf_ptr)) != NULL) {
- if (currmap->max_matches == currmap->n_matches) {
- currmap->max_matches = currmap->max_matches * 2 + 1;
- currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches);
- }
+ if (strcmp(first_word, "mapping") == 0) {
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ currmap = xzalloc(sizeof(*currmap));
- currmap->match[currmap->n_matches++] = bb_xstrdup(firstword);
+ while ((first_word = next_word(&rest_of_line)) != NULL) {
+ currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches);
+ currmap->match[currmap->n_matches++] = xstrdup(first_word);
}
- currmap->max_mappings = 0;
- currmap->n_mappings = 0;
- currmap->mapping = NULL;
- currmap->script = NULL;
+ /*currmap->max_mappings = 0; - done by xzalloc */
+ /*currmap->n_mappings = 0;*/
+ /*currmap->mapping = NULL;*/
+ /*currmap->script = NULL;*/
{
struct mapping_defn_t **where = &defn->mappings;
while (*where != NULL) {
where = &(*where)->next;
}
*where = currmap;
- currmap->next = NULL;
+ /*currmap->next = NULL;*/
}
debug_noise("Added mapping\n");
#endif
currently_processing = MAPPING;
- } else if (strcmp(firstword, "iface") == 0) {
- {
- char *iface_name;
- char *address_family_name;
- char *method_name;
- struct address_family_t *addr_fams[] = {
-#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
- &addr_inet,
-#endif
-#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
- &addr_inet6,
+ } else if (strcmp(first_word, "iface") == 0) {
+ static const struct address_family_t *const addr_fams[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
+ &addr_inet,
#endif
-#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
- &addr_ipx,
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
+ &addr_inet6,
#endif
- NULL
- };
-
- currif = xmalloc(sizeof(struct interface_defn_t));
- iface_name = next_word(&buf_ptr);
- address_family_name = next_word(&buf_ptr);
- method_name = next_word(&buf_ptr);
-
- if (buf_ptr == NULL) {
- bb_error_msg("too few parameters for line \"%s\"", buf);
- return NULL;
- }
-
- /* ship any trailing whitespace */
- while (isspace(*buf_ptr)) {
- ++buf_ptr;
+ NULL
+ };
+ char *iface_name;
+ char *address_family_name;
+ char *method_name;
+ llist_t *iface_list;
+
+ currif = xzalloc(sizeof(*currif));
+ iface_name = next_word(&rest_of_line);
+ address_family_name = next_word(&rest_of_line);
+ method_name = next_word(&rest_of_line);
+
+ if (method_name == NULL)
+ bb_error_msg_and_die("too few parameters for line \"%s\"", buf);
+
+ /* ship any trailing whitespace */
+ rest_of_line = skip_whitespace(rest_of_line);
+
+ if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */)
+ bb_error_msg_and_die("too many parameters \"%s\"", buf);
+
+ currif->iface = xstrdup(iface_name);
+
+ currif->address_family = get_address_family(addr_fams, address_family_name);
+ if (!currif->address_family)
+ bb_error_msg_and_die("unknown address type \"%s\"", address_family_name);
+
+ currif->method = get_method(currif->address_family, method_name);
+ if (!currif->method)
+ bb_error_msg_and_die("unknown method \"%s\"", method_name);
+
+ for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
+ struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
+ if ((strcmp(tmp->iface, currif->iface) == 0)
+ && (tmp->address_family == currif->address_family)
+ ) {
+ bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface);
}
-
- if (buf_ptr[0] != '\0') {
- bb_error_msg("too many parameters \"%s\"", buf);
- return NULL;
- }
-
- currif->iface = bb_xstrdup(iface_name);
-
- currif->address_family = get_address_family(addr_fams, address_family_name);
- if (!currif->address_family) {
- bb_error_msg("unknown address type \"%s\"", buf);
- return NULL;
- }
-
- currif->method = get_method(currif->address_family, method_name);
- if (!currif->method) {
- bb_error_msg("unknown method \"%s\"", buf);
- return NULL;
- }
-
- currif->automatic = 1;
- currif->max_options = 0;
- currif->n_options = 0;
- currif->option = NULL;
-
- {
- struct interface_defn_t *tmp;
- llist_t *iface_list;
- iface_list = defn->ifaces;
- while (iface_list) {
- tmp = (struct interface_defn_t *) iface_list->data;
- if (duplicate_if(tmp, currif)) {
- bb_error_msg("duplicate interface \"%s\"", tmp->iface);
- return NULL;
- }
- iface_list = iface_list->link;
- }
-
- defn->ifaces = llist_add_to_end(defn->ifaces, (char*)currif);
- }
- debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
}
+ llist_add_to_end(&(defn->ifaces), (char*)currif);
+
+ debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
currently_processing = IFACE;
- } else if (strcmp(firstword, "auto") == 0) {
- while ((firstword = next_word(&buf_ptr)) != NULL) {
+ } else if (strcmp(first_word, "auto") == 0) {
+ while ((first_word = next_word(&rest_of_line)) != NULL) {
/* Check the interface isnt already listed */
- if (find_list_string(defn->autointerfaces, firstword)) {
+ if (llist_find_str(defn->autointerfaces, first_word)) {
bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
}
/* Add the interface to the list */
- defn->autointerfaces = llist_add_to_end(defn->autointerfaces, strdup(firstword));
- debug_noise("\nauto %s\n", firstword);
+ llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word));
+ debug_noise("\nauto %s\n", first_word);
}
currently_processing = NONE;
} else {
switch (currently_processing) {
- case IFACE:
- {
- int i;
-
- if (bb_strlen(buf_ptr) == 0) {
- bb_error_msg("option with empty value \"%s\"", buf);
- return NULL;
- }
-
- if (strcmp(firstword, "up") != 0
- && strcmp(firstword, "down") != 0
- && strcmp(firstword, "pre-up") != 0
- && strcmp(firstword, "post-down") != 0) {
- for (i = 0; i < currif->n_options; i++) {
- if (strcmp(currif->option[i].name, firstword) == 0) {
- bb_error_msg("duplicate option \"%s\"", buf);
- return NULL;
- }
- }
- }
- }
- if (currif->n_options >= currif->max_options) {
- struct variable_t *opt;
-
- currif->max_options = currif->max_options + 10;
- opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options);
- currif->option = opt;
- }
- currif->option[currif->n_options].name = bb_xstrdup(firstword);
- currif->option[currif->n_options].value = bb_xstrdup(buf_ptr);
- if (!currif->option[currif->n_options].name) {
- perror(filename);
- return NULL;
- }
- if (!currif->option[currif->n_options].value) {
- perror(filename);
- return NULL;
+ case IFACE:
+ if (rest_of_line[0] == '\0')
+ bb_error_msg_and_die("option with empty value \"%s\"", buf);
+
+ if (strcmp(first_word, "up") != 0
+ && strcmp(first_word, "down") != 0
+ && strcmp(first_word, "pre-up") != 0
+ && strcmp(first_word, "post-down") != 0
+ ) {
+ int i;
+ for (i = 0; i < currif->n_options; i++) {
+ if (strcmp(currif->option[i].name, first_word) == 0)
+ bb_error_msg_and_die("duplicate option \"%s\"", buf);
}
- debug_noise("\t%s=%s\n", currif->option[currif->n_options].name,
- currif->option[currif->n_options].value);
- currif->n_options++;
- break;
- case MAPPING:
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
- if (strcmp(firstword, "script") == 0) {
- if (currmap->script != NULL) {
- bb_error_msg("duplicate script in mapping \"%s\"", buf);
- return NULL;
- } else {
- currmap->script = bb_xstrdup(next_word(&buf_ptr));
- }
- } else if (strcmp(firstword, "map") == 0) {
- if (currmap->max_mappings == currmap->n_mappings) {
- currmap->max_mappings = currmap->max_mappings * 2 + 1;
- currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings);
- }
- currmap->mapping[currmap->n_mappings] = bb_xstrdup(next_word(&buf_ptr));
- currmap->n_mappings++;
- } else {
- bb_error_msg("misplaced option \"%s\"", buf);
- return NULL;
+ }
+ if (currif->n_options >= currif->max_options) {
+ currif->max_options += 10;
+ currif->option = xrealloc(currif->option,
+ sizeof(*currif->option) * currif->max_options);
+ }
+ debug_noise("\t%s=%s\n", first_word, rest_of_line);
+ currif->option[currif->n_options].name = xstrdup(first_word);
+ currif->option[currif->n_options].value = xstrdup(rest_of_line);
+ currif->n_options++;
+ break;
+ case MAPPING:
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ if (strcmp(first_word, "script") == 0) {
+ if (currmap->script != NULL)
+ bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf);
+ currmap->script = xstrdup(next_word(&rest_of_line));
+ } else if (strcmp(first_word, "map") == 0) {
+ if (currmap->n_mappings >= currmap->max_mappings) {
+ currmap->max_mappings = currmap->max_mappings * 2 + 1;
+ currmap->mapping = xrealloc(currmap->mapping,
+ sizeof(char *) * currmap->max_mappings);
}
+ currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
+ currmap->n_mappings++;
+ } else {
+ bb_error_msg_and_die("misplaced option \"%s\"", buf);
+ }
#endif
- break;
- case NONE:
- default:
- bb_error_msg("misplaced option \"%s\"", buf);
- return NULL;
+ break;
+ case NONE:
+ default:
+ bb_error_msg_and_die("misplaced option \"%s\"", buf);
}
}
free(buf);
- }
+ } /* while (fgets) */
+
if (ferror(f) != 0) {
- bb_perror_msg_and_die("%s", filename);
+ /* ferror does NOT set errno! */
+ bb_error_msg_and_die("%s: I/O error", filename);
}
fclose(f);
return defn;
}
-static char *setlocalenv(char *format, char *name, char *value)
+static char *setlocalenv(const char *format, const char *name, const char *value)
{
char *result;
char *here;
char *there;
- result = xmalloc(bb_strlen(format) + bb_strlen(name) + bb_strlen(value) + 1);
-
- sprintf(result, format, name, value);
+ result = xasprintf(format, name, value);
for (here = there = result; *there != '=' && *there; there++) {
if (*there == '-')
@@ -944,222 +913,182 @@ static char *setlocalenv(char *format, char *name, char *value)
here++;
}
}
- memmove(here, there, bb_strlen(there) + 1);
+ memmove(here, there, strlen(there) + 1);
return result;
}
-static void set_environ(struct interface_defn_t *iface, char *mode)
+static void set_environ(struct interface_defn_t *iface, const char *mode)
{
char **environend;
int i;
const int n_env_entries = iface->n_options + 5;
char **ppch;
- if (environ != NULL) {
- for (ppch = environ; *ppch; ppch++) {
+ if (my_environ != NULL) {
+ for (ppch = my_environ; *ppch; ppch++) {
free(*ppch);
*ppch = NULL;
}
- free(environ);
- environ = NULL;
+ free(my_environ);
}
- environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
- environend = environ;
- *environend = NULL;
+ my_environ = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+ environend = my_environ;
for (i = 0; i < iface->n_options; i++) {
if (strcmp(iface->option[i].name, "up") == 0
- || strcmp(iface->option[i].name, "down") == 0
- || strcmp(iface->option[i].name, "pre-up") == 0
- || strcmp(iface->option[i].name, "post-down") == 0) {
+ || strcmp(iface->option[i].name, "down") == 0
+ || strcmp(iface->option[i].name, "pre-up") == 0
+ || strcmp(iface->option[i].name, "post-down") == 0
+ ) {
continue;
}
*(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
- *environend = NULL;
}
*(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface);
- *environend = NULL;
*(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
- *environend = NULL;
*(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name);
- *environend = NULL;
*(environend++) = setlocalenv("%s=%s", "MODE", mode);
- *environend = NULL;
- *(environend++) = setlocalenv("%s=%s", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
- *environend = NULL;
+ *(environend++) = setlocalenv("%s=%s", "PATH", startup_PATH);
}
static int doit(char *str)
{
- if (verbose || no_act) {
- printf("%s\n", str);
+ if (option_mask32 & (OPT_no_act|OPT_verbose)) {
+ puts(str);
}
- if (!no_act) {
+ if (!(option_mask32 & OPT_no_act)) {
pid_t child;
int status;
fflush(NULL);
- switch (child = fork()) {
- case -1: /* failure */
- return 0;
- case 0: /* child */
- execle("/bin/sh", "/bin/sh", "-c", str, NULL, environ);
- exit(127);
+ child = vfork();
+ switch (child) {
+ case -1: /* failure */
+ return 0;
+ case 0: /* child */
+ execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, (char *) NULL, my_environ);
+ _exit(127);
}
- waitpid(child, &status, 0);
+ safe_waitpid(child, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
return 0;
}
}
- return (1);
+ return 1;
}
-static int execute_all(struct interface_defn_t *ifd, execfn *exec, const char *opt)
+static int execute_all(struct interface_defn_t *ifd, const char *opt)
{
int i;
- char *buf[2];
-
+ char *buf;
for (i = 0; i < ifd->n_options; i++) {
if (strcmp(ifd->option[i].name, opt) == 0) {
- if (!(*exec) (ifd->option[i].value)) {
+ if (!doit(ifd->option[i].value)) {
return 0;
}
}
}
- bb_xasprintf(&buf[0], "/etc/network/if-%s.d", opt);
- buf[1] = NULL;
-
- run_parts(buf, 2, environ);
- free(buf[0]);
- return (1);
+ buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
+ /* heh, we don't bother free'ing it */
+ return doit(buf);
}
-static int check(char *str) {
+static int check(char *str)
+{
return str != NULL;
}
static int iface_up(struct interface_defn_t *iface)
{
- int result;
- if (!iface->method->up(iface,check)) return -1;
+ if (!iface->method->up(iface, check)) return -1;
set_environ(iface, "start");
- result = execute_all(iface, doit, "pre-up");
- result += iface->method->up(iface, doit);
- result += execute_all(iface, doit, "up");
- return(result);
+ if (!execute_all(iface, "pre-up")) return 0;
+ if (!iface->method->up(iface, doit)) return 0;
+ if (!execute_all(iface, "up")) return 0;
+ return 1;
}
static int iface_down(struct interface_defn_t *iface)
{
- int result;
if (!iface->method->down(iface,check)) return -1;
set_environ(iface, "stop");
- result = execute_all(iface, doit, "down");
- result += iface->method->down(iface, doit);
- result += execute_all(iface, doit, "post-down");
- return(result);
+ if (!execute_all(iface, "down")) return 0;
+ if (!iface->method->down(iface, doit)) return 0;
+ if (!execute_all(iface, "post-down")) return 0;
+ return 1;
}
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
-static int popen2(FILE **in, FILE **out, char *command, ...)
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+static int popen2(FILE **in, FILE **out, char *command, char *param)
{
- va_list ap;
- char *argv[11] = { command };
- int argc;
- int infd[2], outfd[2];
+ char *argv[3] = { command, param, NULL };
+ struct fd_pair infd, outfd;
pid_t pid;
- argc = 1;
- va_start(ap, command);
- while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) {
- argc++;
- }
- argv[argc] = NULL; /* make sure */
- va_end(ap);
-
- if (pipe(infd) != 0) {
- return 0;
- }
-
- if (pipe(outfd) != 0) {
- close(infd[0]);
- close(infd[1]);
- return 0;
- }
+ xpiped_pair(infd);
+ xpiped_pair(outfd);
fflush(NULL);
- switch (pid = fork()) {
- case -1: /* failure */
- close(infd[0]);
- close(infd[1]);
- close(outfd[0]);
- close(outfd[1]);
- return 0;
- case 0: /* child */
- dup2(infd[0], 0);
- dup2(outfd[1], 1);
- close(infd[0]);
- close(infd[1]);
- close(outfd[0]);
- close(outfd[1]);
- execvp(command, argv);
- exit(127);
- default: /* parent */
- *in = fdopen(infd[1], "w");
- *out = fdopen(outfd[0], "r");
- close(infd[0]);
- close(outfd[1]);
- return pid;
+ pid = vfork();
+
+ switch (pid) {
+ case -1: /* failure */
+ bb_perror_msg_and_die("vfork");
+ case 0: /* child */
+ /* NB: close _first_, then move fds! */
+ close(infd.wr);
+ close(outfd.rd);
+ xmove_fd(infd.rd, 0);
+ xmove_fd(outfd.wr, 1);
+ BB_EXECVP(command, argv);
+ _exit(127);
}
- /* unreached */
+ /* parent */
+ close(infd.rd);
+ close(outfd.wr);
+ *in = fdopen(infd.wr, "w");
+ *out = fdopen(outfd.rd, "r");
+ return pid;
}
-static char * run_mapping(char *physical, struct mapping_defn_t * map)
+static char *run_mapping(char *physical, struct mapping_defn_t *map)
{
FILE *in, *out;
int i, status;
pid_t pid;
- char *logical = bb_xstrdup(physical);
-
- /* Run the mapping script. */
- pid = popen2(&in, &out, map->script, physical, NULL);
+ char *logical = xstrdup(physical);
- /* popen2() returns 0 on failure. */
- if (pid == 0)
- return logical;
+ /* Run the mapping script. Never fails. */
+ pid = popen2(&in, &out, map->script, physical);
/* Write mappings to stdin of mapping script. */
for (i = 0; i < map->n_mappings; i++) {
fprintf(in, "%s\n", map->mapping[i]);
}
fclose(in);
- waitpid(pid, &status, 0);
+ safe_waitpid(pid, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
/* If the mapping script exited successfully, try to
* grab a line of output and use that as the name of the
* logical interface. */
- char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH);
+ char *new_logical = xmalloc_fgetline(out);
- if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {
+ if (new_logical) {
/* If we are able to read a line of output from the script,
* remove any trailing whitespace and use this value
* as the name of the logical interface. */
- char *pch = new_logical + bb_strlen(new_logical) - 1;
+ char *pch = new_logical + strlen(new_logical) - 1;
while (pch >= new_logical && isspace(*pch))
*(pch--) = '\0';
free(logical);
logical = new_logical;
- } else {
- /* If we are UNABLE to read a line of output, discard are
- * freshly allocated memory. */
- free(new_logical);
}
}
@@ -1167,144 +1096,80 @@ static char * run_mapping(char *physical, struct mapping_defn_t * map)
return logical;
}
-#endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */
+#endif /* FEATURE_IFUPDOWN_MAPPING */
static llist_t *find_iface_state(llist_t *state_list, const char *iface)
{
- unsigned short iface_len = bb_strlen(iface);
+ unsigned iface_len = strlen(iface);
llist_t *search = state_list;
while (search) {
- if ((strncmp(search->data, iface, iface_len) == 0) &&
- (search->data[iface_len] == '=')) {
- return(search);
+ if ((strncmp(search->data, iface, iface_len) == 0)
+ && (search->data[iface_len] == '=')
+ ) {
+ return search;
}
search = search->link;
}
- return(NULL);
+ return NULL;
}
-extern int ifupdown_main(int argc, char **argv)
+/* read the previous state from the state file */
+static llist_t *read_iface_state(void)
{
- int (*cmds) (struct interface_defn_t *) = NULL;
- struct interfaces_file_t *defn;
- FILE *state_fp = NULL;
llist_t *state_list = NULL;
- llist_t *target_list = NULL;
- char *interfaces = "/etc/network/interfaces";
- const char *statefile = "/var/run/ifstate";
+ FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH);
+
+ if (state_fp) {
+ char *start, *end_ptr;
+ while ((start = xmalloc_fgets(state_fp)) != NULL) {
+ /* We should only need to check for a single character */
+ end_ptr = start + strcspn(start, " \t\n");
+ *end_ptr = '\0';
+ llist_add_to(&state_list, start);
+ }
+ fclose(state_fp);
+ }
+ return state_list;
+}
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
- int run_mappings = 1;
-#endif
- int do_all = 0;
- int force = 0;
- int i;
- if (bb_applet_name[2] == 'u') {
+int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ifupdown_main(int argc, char **argv)
+{
+ int (*cmds)(struct interface_defn_t *);
+ struct interfaces_file_t *defn;
+ llist_t *target_list = NULL;
+ const char *interfaces = "/etc/network/interfaces";
+ bool any_failures = 0;
+
+ cmds = iface_down;
+ if (applet_name[2] == 'u') {
/* ifup command */
cmds = iface_up;
- } else {
- /* ifdown command */
- cmds = iface_down;
}
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
- while ((i = getopt(argc, argv, "i:hvnamf")) != -1)
-#else
- while ((i = getopt(argc, argv, "i:hvnaf")) != -1)
-#endif
- {
- switch (i) {
- case 'i': /* interfaces */
- interfaces = bb_xstrdup(optarg);
- break;
- case 'v': /* verbose */
- verbose = 1;
- break;
- case 'a': /* all */
- do_all = 1;
- break;
- case 'n': /* no-act */
- no_act = 1;
- break;
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
- case 'm': /* no-mappings */
- run_mappings = 0;
- break;
-#endif
- case 'f': /* force */
- force = 1;
- break;
- default:
- bb_show_usage();
- break;
- }
- }
-
+ getopt32(argv, OPTION_STR, &interfaces);
if (argc - optind > 0) {
- if (do_all) {
- bb_show_usage();
- }
+ if (DO_ALL) bb_show_usage();
} else {
- if (!do_all) {
- bb_show_usage();
- }
- }
+ if (!DO_ALL) bb_show_usage();
+ }
debug_noise("reading %s file:\n", interfaces);
defn = read_interfaces(interfaces);
debug_noise("\ndone reading %s\n\n", interfaces);
- if (!defn) {
- exit(EXIT_FAILURE);
- }
-
- if (no_act) {
- state_fp = fopen(statefile, "r");
- }
+ startup_PATH = getenv("PATH");
+ if (!startup_PATH) startup_PATH = "";
/* Create a list of interfaces to work on */
- if (do_all) {
- if (cmds == iface_up) {
- target_list = defn->autointerfaces;
- } else {
-#if 0
- /* iface_down */
- llist_t *new_item;
- const llist_t *list = state_list;
- while (list) {
- new_item = xmalloc(sizeof(llist_t));
- new_item->data = strdup(list->data);
- new_item->link = NULL;
- list = target_list;
- if (list == NULL)
- target_list = new_item;
- else {
- while (list->link) {
- list = list->link;
- }
- list = new_item;
- }
- list = list->link;
- }
- target_list = defn->autointerfaces;
-#else
-
- /* iface_down */
- const llist_t *list = state_list;
- while (list) {
- target_list = llist_add_to_end(target_list, strdup(list->data));
- list = list->link;
- }
- target_list = defn->autointerfaces;
-#endif
- }
+ if (DO_ALL) {
+ target_list = defn->autointerfaces;
} else {
- target_list = llist_add_to_end(target_list, argv[optind]);
+ llist_add_to_end(&target_list, argv[optind]);
}
-
/* Update the interfaces */
while (target_list) {
llist_t *iface_list;
@@ -1312,20 +1177,22 @@ extern int ifupdown_main(int argc, char **argv)
char *iface;
char *liface;
char *pch;
- int okay = 0;
+ bool okay = 0;
+ int cmds_ret;
- iface = strdup(target_list->data);
+ iface = xstrdup(target_list->data);
target_list = target_list->link;
pch = strchr(iface, '=');
if (pch) {
*pch = '\0';
- liface = strdup(pch + 1);
+ liface = xstrdup(pch + 1);
} else {
- liface = strdup(iface);
+ liface = xstrdup(iface);
}
- if (!force) {
+ if (!FORCE) {
+ llist_t *state_list = read_iface_state();
const llist_t *iface_state = find_iface_state(state_list, iface);
if (cmds == iface_up) {
@@ -1336,23 +1203,24 @@ extern int ifupdown_main(int argc, char **argv)
}
} else {
/* ifdown */
- if (iface_state) {
+ if (!iface_state) {
bb_error_msg("interface %s not configured", iface);
continue;
}
}
+ llist_free(state_list, free);
}
-#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
- if ((cmds == iface_up) && run_mappings) {
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ if ((cmds == iface_up) && !NO_MAPPINGS) {
struct mapping_defn_t *currmap;
for (currmap = defn->mappings; currmap; currmap = currmap->next) {
-
+ int i;
for (i = 0; i < currmap->n_matches; i++) {
if (fnmatch(currmap->match[i], liface, 0) != 0)
continue;
- if (verbose) {
+ if (VERBOSE) {
printf("Running mapping script %s on %s\n", currmap->script, liface);
}
liface = run_mapping(iface, currmap);
@@ -1362,7 +1230,6 @@ extern int ifupdown_main(int argc, char **argv)
}
#endif
-
iface_list = defn->ifaces;
while (iface_list) {
currif = (struct interface_defn_t *) iface_list->data;
@@ -1375,79 +1242,60 @@ extern int ifupdown_main(int argc, char **argv)
debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
/* Call the cmds function pointer, does either iface_up() or iface_down() */
- if (cmds(currif) == -1) {
- bb_error_msg("Don't seem to be have all the variables for %s/%s.",
+ cmds_ret = cmds(currif);
+ if (cmds_ret == -1) {
+ bb_error_msg("don't seem to have all the variables for %s/%s",
liface, currif->address_family->name);
+ any_failures = 1;
+ } else if (cmds_ret == 0) {
+ any_failures = 1;
}
currif->iface = oldiface;
}
iface_list = iface_list->link;
}
- if (verbose) {
- printf("\n");
+ if (VERBOSE) {
+ bb_putchar('\n');
}
- if (!okay && !force) {
- bb_error_msg("Ignoring unknown interface %s", liface);
- } else {
+ if (!okay && !FORCE) {
+ bb_error_msg("ignoring unknown interface %s", liface);
+ any_failures = 1;
+ } else if (!NO_ACT) {
+ /* update the state file */
+ FILE *state_fp;
+ llist_t *state;
+ llist_t *state_list = read_iface_state();
llist_t *iface_state = find_iface_state(state_list, iface);
if (cmds == iface_up) {
- char *newiface = xmalloc(bb_strlen(iface) + 1 + bb_strlen(liface) + 1);
- sprintf(newiface, "%s=%s", iface, liface);
+ char * const newiface = xasprintf("%s=%s", iface, liface);
if (iface_state == NULL) {
- state_list = llist_add_to_end(state_list, newiface);
+ llist_add_to_end(&state_list, newiface);
} else {
free(iface_state->data);
iface_state->data = newiface;
}
- } else if (cmds == iface_down) {
- /* Remove an interface from the linked list */
- if (iface_state) {
- /* This needs to be done better */
- free(iface_state->data);
- free(iface_state->link);
- if (iface_state->link) {
- iface_state->data = iface_state->link->data;
- iface_state->link = iface_state->link->link;
- } else {
- iface_state->data = NULL;
- iface_state->link = NULL;
- }
- }
+ } else {
+ /* Remove an interface from state_list */
+ llist_unlink(&state_list, iface_state);
+ free(llist_pop(&iface_state));
}
- }
- }
-
- /* Actually write the new state */
- if (!no_act) {
-
- if (state_fp)
- fclose(state_fp);
- state_fp = bb_xfopen(statefile, "a+");
- if (ftruncate(fileno(state_fp), 0) < 0) {
- bb_error_msg_and_die("failed to truncate statefile %s: %s", statefile, strerror(errno));
- }
-
- rewind(state_fp);
-
- while (state_list) {
- if (state_list->data) {
- fputs(state_list->data, state_fp);
- fputc('\n', state_fp);
+ /* Actually write the new state */
+ state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
+ state = state_list;
+ while (state) {
+ if (state->data) {
+ fprintf(state_fp, "%s\n", state->data);
+ }
+ state = state->link;
}
- state_list = state_list->link;
+ fclose(state_fp);
+ llist_free(state_list, free);
}
- fflush(state_fp);
}
- /* Cleanup */
- if (state_fp != NULL) {
- fclose(state_fp);
- state_fp = NULL;
- }
-
- return 0;
+ return any_failures;
}
diff --git a/release/src/router/busybox/networking/inetd.c b/release/src/router/busybox/networking/inetd.c
index 4c46495a..590bf23d 100644
--- a/release/src/router/busybox/networking/inetd.c
+++ b/release/src/router/busybox/networking/inetd.c
@@ -1,1220 +1,1534 @@
+/* vi: set sw=4 ts=4: */
+/* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */
+/* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */
+/* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */
+/* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */
+/* IPv6 support, many bug fixes by Denys Vlasenko (c) 2008 */
/*
* Copyright (c) 1983,1991 The Regents of the University of California.
* All rights reserved.
*
- * This code is derived from software contributed to Berkeley by
- * David A. Holland.
- *
- * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
*/
-/*
- * Inetd - Internet super-server
+/* Inetd - Internet super-server
*
- * This program invokes all internet services as needed.
- * connection-oriented services are invoked each time a
+ * This program invokes configured services when a connection
+ * from a peer is established or a datagram arrives.
+ * Connection-oriented services are invoked each time a
* connection is made, by creating a process. This process
* is passed the connection as file descriptor 0 and is
- * expected to do a getpeername to find out the source host
+ * expected to do a getpeername to find out peer's host
* and port.
- *
* Datagram oriented services are invoked when a datagram
* arrives; a process is created and passed a pending message
- * on file descriptor 0. Datagram servers may either connect
- * to their peer, freeing up the original socket for inetd
- * to receive further messages on, or ``take over the socket'',
- * processing all arriving datagrams and, eventually, timing
- * out. The first type of server is said to be ``multi-threaded'';
- * the second type of server ``single-threaded''.
+ * on file descriptor 0. peer's address can be obtained
+ * using recvfrom.
*
* Inetd uses a configuration file which is read at startup
* and, possibly, at some later time in response to a hangup signal.
- * The configuration file is ``free format'' with fields given in the
- * order shown below. Continuation lines for an entry must being with
+ * The configuration file is "free format" with fields given in the
+ * order shown below. Continuation lines for an entry must begin with
* a space or tab. All fields must be present in each entry.
*
- * service name must be in /etc/services
- * socket type stream/dgram/raw/rdm/seqpacket
+ * service_name must be in /etc/services
+ * socket_type stream/dgram/raw/rdm/seqpacket
* protocol must be in /etc/protocols
+ * (usually "tcp" or "udp")
* wait/nowait[.max] single-threaded/multi-threaded, max #
- * user[.group] user/group to run daemon as
- * server program full path name
- * server program arguments maximum of MAXARGS (20)
- *
- * RPC services unsuported
+ * user[.group] or user[:group] user/group to run daemon as
+ * server_program full path name
+ * server_program_arguments maximum of MAXARGS (20)
*
- * Comment lines are indicated by a `#' in column 1.
- */
-
-/*
- * Here's the scoop concerning the user.group feature:
+ * For RPC services
+ * service_name/version must be in /etc/rpc
+ * socket_type stream/dgram/raw/rdm/seqpacket
+ * rpc/protocol "rpc/tcp" etc
+ * wait/nowait[.max] single-threaded/multi-threaded
+ * user[.group] or user[:group] user to run daemon as
+ * server_program full path name
+ * server_program_arguments maximum of MAXARGS (20)
*
- * 1) No group listed.
+ * For non-RPC services, the "service name" can be of the form
+ * hostaddress:servicename, in which case the hostaddress is used
+ * as the host portion of the address to listen on. If hostaddress
+ * consists of a single '*' character, INADDR_ANY is used.
*
- * a) for root: NO setuid() or setgid() is done
+ * A line can also consist of just
+ * hostaddress:
+ * where hostaddress is as in the preceding paragraph. Such a line must
+ * have no further fields; the specified hostaddress is remembered and
+ * used for all further lines that have no hostaddress specified,
+ * until the next such line (or EOF). (This is why * is provided to
+ * allow explicit specification of INADDR_ANY.) A line
+ * *:
+ * is implicitly in effect at the beginning of the file.
*
- * b) nonroot: setuid()
- * setgid(primary group as found in passwd)
- * initgroups(name, primary group)
+ * The hostaddress specifier may (and often will) contain dots;
+ * the service name must not.
*
- * 2) set-group-option on.
+ * For RPC services, host-address specifiers are accepted and will
+ * work to some extent; however, because of limitations in the
+ * portmapper interface, it will not work to try to give more than
+ * one line for any given RPC service, even if the host-address
+ * specifiers are different.
*
- * a) for root: NO setuid()
- * setgid(specified group)
- * setgroups(1, specified group)
+ * Comment lines are indicated by a '#' in column 1.
+ */
+
+/* inetd rules for passing file descriptors to children
+ * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
*
- * b) nonroot: setuid()
- * setgid(specified group)
- * initgroups(name, specified group)
+ * The wait/nowait entry specifies whether the server that is invoked by
+ * inetd will take over the socket associated with the service access point,
+ * and thus whether inetd should wait for the server to exit before listen-
+ * ing for new service requests. Datagram servers must use "wait", as
+ * they are always invoked with the original datagram socket bound to the
+ * specified service address. These servers must read at least one datagram
+ * from the socket before exiting. If a datagram server connects to its
+ * peer, freeing the socket so inetd can receive further messages on the
+ * socket, it is said to be a "multi-threaded" server; it should read one
+ * datagram from the socket and create a new socket connected to the peer.
+ * It should fork, and the parent should then exit to allow inetd to check
+ * for new service requests to spawn new servers. Datagram servers which
+ * process all incoming datagrams on a socket and eventually time out are
+ * said to be "single-threaded". The comsat(8), biff(1) and talkd(8)
+ * utilities are both examples of the latter type of datagram server. The
+ * tftpd(8) utility is an example of a multi-threaded datagram server.
*
- * All supplementary groups are discarded at startup in case inetd was
- * run manually.
+ * Servers using stream sockets generally are multi-threaded and use the
+ * "nowait" entry. Connection requests for these services are accepted by
+ * inetd, and the server is given only the newly-accepted socket connected
+ * to a client of the service. Most stream-based services operate in this
+ * manner. Stream-based servers that use "wait" are started with the lis-
+ * tening service socket, and must accept at least one connection request
+ * before exiting. Such a server would normally accept and process incoming
+ * connection requests until a timeout.
*/
-#define __USE_BSD_SIGNAL
+/* Despite of above doc saying that dgram services must use "wait",
+ * "udp nowait" servers are implemented in busyboxed inetd.
+ * IPv6 addresses are also implemented. However, they may look ugly -
+ * ":::service..." means "address '::' (IPv6 wildcard addr)":"service"...
+ * You have to put "tcp6"/"udp6" in protocol field to select IPv6.
+ */
-#include "busybox.h"
+/* Here's the scoop concerning the user[:group] feature:
+ * 1) group is not specified:
+ * a) user = root: NO setuid() or setgid() is done
+ * b) other: initgroups(name, primary group)
+ * setgid(primary group as found in passwd)
+ * setuid()
+ * 2) group is specified:
+ * a) user = root: setgid(specified group)
+ * NO initgroups()
+ * NO setuid()
+ * b) other: initgroups(name, specified group)
+ * setgid(specified group)
+ * setuid()
+ */
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
+#include <syslog.h>
#include <sys/un.h>
-#include <sys/file.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#ifndef __linux__
-#ifndef RLIMIT_NOFILE
-#define RLIMIT_NOFILE RLIMIT_OFILE
+#include "libbb.h"
+
+#if ENABLE_FEATURE_INETD_RPC
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
#endif
+
+#if !BB_MMU
+/* stream version of chargen is forking but not execing,
+ * can't do that (easily) on NOMMU */
+#undef ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0
#endif
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/file.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <arpa/inet.h>
-
-#include <errno.h>
-#include <signal.h>
-#include <netdb.h>
-#include <syslog.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <time.h>
-
-#define _PATH_INETDCONF "/etc/inetd.conf"
#define _PATH_INETDPID "/var/run/inetd.pid"
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#endif
+#define CNT_INTERVAL 60 /* servers in CNT_INTERVAL sec. */
+#define RETRYTIME 60 /* retry after bind or server fail */
+
+// TODO: explain, or get rid of setrlimit games
-#define TOOMANY 40 /* don't start more than TOOMANY */
-#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
-#define RETRYTIME (60*10) /* retry after bind or server fail */
+#ifndef RLIMIT_NOFILE
+#define RLIMIT_NOFILE RLIMIT_OFILE
+#endif
#ifndef OPEN_MAX
#define OPEN_MAX 64
#endif
-
/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
-#define FD_MARGIN (8)
-static int rlim_ofile_cur = OPEN_MAX;
-
-#ifdef RLIMIT_NOFILE
-static struct rlimit rlim_ofile;
+#define FD_MARGIN 8
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+# define INETD_BUILTINS_ENABLED
#endif
-#define INETD_UNSUPPORT_BILTIN 1
-
-/* Check unsupporting builtin */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
-#undef INETD_UNSUPPORT_BILTIN
-#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
-#undef INETD_UNSUPPORT_BILTIN
-#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
-#undef INETD_UNSUPPORT_BILTIN
-#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
-#undef INETD_UNSUPPORT_BILTIN
-#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
-#undef INETD_UNSUPPORT_BILTIN
+typedef struct servtab_t {
+ /* The most frequently referenced one: */
+ int se_fd; /* open descriptor */
+ /* NB: 'biggest fields last' saves on code size (~250 bytes) */
+ /* [addr:]service socktype proto wait user[:group] prog [args] */
+ char *se_local_hostname; /* addr to listen on */
+ char *se_service; /* "80" or "www" or "mount/2[-3]" */
+ /* socktype is in se_socktype */ /* "stream" "dgram" "raw" "rdm" "seqpacket" */
+ char *se_proto; /* "unix" or "[rpc/]tcp[6]" */
+#if ENABLE_FEATURE_INETD_RPC
+ int se_rpcprog; /* rpc program number */
+ int se_rpcver_lo; /* rpc program lowest version */
+ int se_rpcver_hi; /* rpc program highest version */
+#define is_rpc_service(sep) ((sep)->se_rpcver_lo != 0)
+#else
+#define is_rpc_service(sep) 0
#endif
-
-static struct servtab {
- char *se_service; /* name of service */
- int se_socktype; /* type of socket to use */
- int se_family; /* address family */
- char *se_proto; /* protocol used */
- short se_wait; /* single threaded server */
- short se_checked; /* looked at during merge */
- char *se_user; /* user name to run as */
- char *se_group; /* group name to run as */
-#ifndef INETD_UNSUPPORT_BILTIN
- const struct biltin *se_bi; /* if built-in, description */
+ pid_t se_wait; /* 0:"nowait", 1:"wait", >1:"wait" */
+ /* and waiting for this pid */
+ socktype_t se_socktype; /* SOCK_STREAM/DGRAM/RDM/... */
+ family_t se_family; /* AF_UNIX/INET[6] */
+ /* se_proto_no is used by RPC code only... hmm */
+ smallint se_proto_no; /* IPPROTO_TCP/UDP, n/a for AF_UNIX */
+ smallint se_checked; /* looked at during merge */
+ unsigned se_max; /* allowed instances per minute */
+ unsigned se_count; /* number started since se_time */
+ unsigned se_time; /* when we started counting */
+ char *se_user; /* user name to run as */
+ char *se_group; /* group name to run as, can be NULL */
+#ifdef INETD_BUILTINS_ENABLED
+ const struct builtin *se_builtin; /* if built-in, description */
#endif
- char *se_server; /* server program */
+ struct servtab_t *se_next;
+ len_and_sockaddr *se_lsa;
+ char *se_program; /* server program */
#define MAXARGV 20
- char *se_argv[MAXARGV+1]; /* program arguments */
- int se_fd; /* open descriptor */
- union {
- struct sockaddr se_un_ctrladdr;
- struct sockaddr_in se_un_ctrladdr_in;
- struct sockaddr_un se_un_ctrladdr_un;
- } se_un; /* bound address */
-#define se_ctrladdr se_un.se_un_ctrladdr
-#define se_ctrladdr_in se_un.se_un_ctrladdr_in
-#define se_ctrladdr_un se_un.se_un_ctrladdr_un
- int se_ctrladdr_size;
- int se_max; /* max # of instances of this service */
- int se_count; /* number started since se_time */
- struct timeval se_time; /* start of se_count */
- struct servtab *se_next;
-} *servtab;
-
-/* Length of socket listen queue. Should be per-service probably. */
-static int global_queuelen = 128;
-
-static int nsock, maxsock;
-static fd_set allsock;
-static int timingout;
-static sigset_t blockmask, emptymask;
-
-
- /* Echo received data */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
-static void echo_stream(int, struct servtab *);
-static void echo_dg(int, struct servtab *);
+ char *se_argv[MAXARGV + 1]; /* program arguments */
+} servtab_t;
+
+#ifdef INETD_BUILTINS_ENABLED
+/* Echo received data */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+static void echo_stream(int, servtab_t *);
+static void echo_dg(int, servtab_t *);
#endif
- /* Internet /dev/null */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
-static void discard_stream(int, struct servtab *);
-static void discard_dg(int, struct servtab *);
+/* Internet /dev/null */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+static void discard_stream(int, servtab_t *);
+static void discard_dg(int, servtab_t *);
#endif
- /* Return 32 bit time since 1900 */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
-static void machtime_stream(int, struct servtab *);
-static void machtime_dg(int, struct servtab *);
+/* Return 32 bit time since 1900 */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+static void machtime_stream(int, servtab_t *);
+static void machtime_dg(int, servtab_t *);
#endif
- /* Return human-readable time */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
-static void daytime_stream(int, struct servtab *);
-static void daytime_dg(int, struct servtab *);
+/* Return human-readable time */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+static void daytime_stream(int, servtab_t *);
+static void daytime_dg(int, servtab_t *);
#endif
- /* Familiar character generator */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
-static void chargen_stream(int, struct servtab *);
-static void chargen_dg(int, struct servtab *);
+/* Familiar character generator */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+static void chargen_stream(int, servtab_t *);
+static void chargen_dg(int, servtab_t *);
#endif
-#ifndef INETD_UNSUPPORT_BILTIN
-struct biltin {
- const char *bi_service; /* internally provided service name */
- int bi_socktype; /* type of socket supported */
- short bi_fork; /* 1 if should fork before call */
- short bi_wait; /* 1 if should wait for child */
- void (*bi_fn)(int, struct servtab *); /* fn which performs it */
+struct builtin {
+ /* NB: not necessarily NUL terminated */
+ char bi_service7[7]; /* internally provided service name */
+ uint8_t bi_fork; /* 1 if stream fn should run in child */
+ void (*bi_stream_fn)(int, servtab_t *);
+ void (*bi_dgram_fn)(int, servtab_t *);
};
-static const struct biltin biltins[] = {
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
- /* Echo received data */
- { "echo", SOCK_STREAM, 1, 0, echo_stream, },
- { "echo", SOCK_DGRAM, 0, 0, echo_dg, },
+static const struct builtin builtins[] = {
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+ { "echo", 1, echo_stream, echo_dg },
#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
- /* Internet /dev/null */
- { "discard", SOCK_STREAM, 1, 0, discard_stream, },
- { "discard", SOCK_DGRAM, 0, 0, discard_dg, },
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+ { "discard", 1, discard_stream, discard_dg },
#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
- /* Return 32 bit time since 1900 */
- { "time", SOCK_STREAM, 0, 0, machtime_stream, },
- { "time", SOCK_DGRAM, 0, 0, machtime_dg, },
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+ { "chargen", 1, chargen_stream, chargen_dg },
#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
- /* Return human-readable time */
- { "daytime", SOCK_STREAM, 0, 0, daytime_stream, },
- { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, },
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+ { "time", 0, machtime_stream, machtime_dg },
#endif
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
- /* Familiar character generator */
- { "chargen", SOCK_STREAM, 1, 0, chargen_stream, },
- { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, },
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+ { "daytime", 0, daytime_stream, daytime_dg },
#endif
- { NULL, 0, 0, 0, NULL }
};
-#endif /* INETD_UNSUPPORT_BILTIN */
-
-#define NUMINT (sizeof(intab) / sizeof(struct inent))
-static const char *CONFIG = _PATH_INETDCONF;
+#endif /* INETD_BUILTINS_ENABLED */
+
+struct globals {
+ rlim_t rlim_ofile_cur;
+ struct rlimit rlim_ofile;
+ servtab_t *serv_list;
+ int global_queuelen;
+ int maxsock; /* max fd# in allsock, -1: unknown */
+ /* whenever maxsock grows, prev_maxsock is set to new maxsock,
+ * but if maxsock is set to -1, prev_maxsock is not changed */
+ int prev_maxsock;
+ unsigned max_concurrency;
+ smallint alarm_armed;
+ uid_t real_uid; /* user ID who ran us */
+ const char *config_filename;
+ parser_t *parser;
+ char *default_local_hostname;
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+ char *end_ring;
+ char *ring_pos;
+ char ring[128];
+#endif
+ fd_set allsock;
+ /* Used in next_line(), and as scratch read buffer */
+ char line[256]; /* _at least_ 256, see LINE_SIZE */
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) };
+struct BUG_G_too_big {
+ char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
+#define rlim_ofile_cur (G.rlim_ofile_cur )
+#define rlim_ofile (G.rlim_ofile )
+#define serv_list (G.serv_list )
+#define global_queuelen (G.global_queuelen)
+#define maxsock (G.maxsock )
+#define prev_maxsock (G.prev_maxsock )
+#define max_concurrency (G.max_concurrency)
+#define alarm_armed (G.alarm_armed )
+#define real_uid (G.real_uid )
+#define config_filename (G.config_filename)
+#define parser (G.parser )
+#define default_local_hostname (G.default_local_hostname)
+#define first_ps_byte (G.first_ps_byte )
+#define last_ps_byte (G.last_ps_byte )
+#define end_ring (G.end_ring )
+#define ring_pos (G.ring_pos )
+#define ring (G.ring )
+#define allsock (G.allsock )
+#define line (G.line )
+#define INIT_G() do { \
+ rlim_ofile_cur = OPEN_MAX; \
+ global_queuelen = 128; \
+ config_filename = "/etc/inetd.conf"; \
+} while (0)
+
+static void maybe_close(int fd)
+{
+ if (fd >= 0)
+ close(fd);
+}
-#define BCOPY(s, d, z) memcpy(d, s, z)
+// TODO: move to libbb?
+static len_and_sockaddr *xzalloc_lsa(int family)
+{
+ len_and_sockaddr *lsa;
+ int sz;
+
+ sz = sizeof(struct sockaddr_in);
+ if (family == AF_UNIX)
+ sz = sizeof(struct sockaddr_un);
+#if ENABLE_FEATURE_IPV6
+ if (family == AF_INET6)
+ sz = sizeof(struct sockaddr_in6);
+#endif
+ lsa = xzalloc(LSA_LEN_SIZE + sz);
+ lsa->len = sz;
+ lsa->u.sa.sa_family = family;
+ return lsa;
+}
-static void
-syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
- __attribute__ ((noreturn, format (printf, 2, 3)));
+static void rearm_alarm(void)
+{
+ if (!alarm_armed) {
+ alarm_armed = 1;
+ alarm(RETRYTIME);
+ }
+}
-static void
-syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
+static void block_CHLD_HUP_ALRM(sigset_t *m)
{
- char buf[50];
- va_list p;
-
- va_start(p, msg);
- vsyslog(LOG_ERR, msg, p);
- if (se_socktype != SOCK_STREAM)
- recv(0, buf, sizeof (buf), 0);
- _exit(1);
+ sigemptyset(m);
+ sigaddset(m, SIGCHLD);
+ sigaddset(m, SIGHUP);
+ sigaddset(m, SIGALRM);
+ sigprocmask(SIG_BLOCK, m, m); /* old sigmask is stored in m */
}
-static FILE *fconfig;
-static char line[256];
+static void restore_sigmask(sigset_t *m)
+{
+ sigprocmask(SIG_SETMASK, m, NULL);
+}
-static FILE *
-setconfig(void)
+#if ENABLE_FEATURE_INETD_RPC
+static void register_rpc(servtab_t *sep)
{
- FILE *f = fconfig;
+ int n;
+ struct sockaddr_in ir_sin;
+ socklen_t size;
- if (f != NULL) {
- fseek(f, 0L, L_SET);
- } else {
- f = fconfig = fopen(CONFIG, "r");
- if(f == NULL)
- syslog(LOG_ERR, "%s: %m", CONFIG);
+ size = sizeof(ir_sin);
+ if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
+ bb_perror_msg("getsockname");
+ return;
}
- return f;
-}
-static char *
-nextline(void)
-{
- char *cp;
- FILE *fd = fconfig;
-
- if (fgets(line, sizeof (line), fd) == NULL)
- return ((char *)0);
- cp = strchr(line, '\n');
- if (cp)
- *cp = '\0';
- return (line);
+ for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
+ pmap_unset(sep->se_rpcprog, n);
+ if (!pmap_set(sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port)))
+ bb_perror_msg("%s %s: pmap_set(%u,%u,%u,%u)",
+ sep->se_service, sep->se_proto,
+ sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port));
+ }
}
-static char *
-skip(char **cpp)
+static void unregister_rpc(servtab_t *sep)
{
- char *cp = *cpp;
- char *start;
-
- if (*cpp == NULL)
- return ((char *)0);
-
-again:
- while (*cp == ' ' || *cp == '\t')
- cp++;
- if (*cp == '\0') {
- int c;
-
- c = getc(fconfig);
- (void) ungetc(c, fconfig);
- if (c == ' ' || c == '\t')
- if ((cp = nextline()) != NULL)
- goto again;
- *cpp = NULL;
- return NULL;
+ int n;
+
+ for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
+ if (!pmap_unset(sep->se_rpcprog, n))
+ bb_perror_msg("pmap_unset(%u,%u)", sep->se_rpcprog, n);
}
- start = cp;
- while (*cp && *cp != ' ' && *cp != '\t')
- cp++;
- if (*cp != '\0')
- *cp++ = '\0';
- *cpp = cp;
- return (start);
}
+#endif /* FEATURE_INETD_RPC */
-static char *
-newstr(char *cp)
+static void bump_nofile(void)
{
- cp = strdup(cp ? cp : "");
- if (cp)
- return(cp);
+ enum { FD_CHUNK = 32 };
+ struct rlimit rl;
- syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m");
-}
+ /* Never fails under Linux (except if you pass it bad arguments) */
+ getrlimit(RLIMIT_NOFILE, &rl);
+ rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
+ rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
+ if (rl.rlim_cur <= rlim_ofile_cur) {
+ bb_error_msg("can't extend file limit, max = %d",
+ (int) rl.rlim_cur);
+ return;
+ }
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ bb_perror_msg("setrlimit");
+ return;
+ }
-static struct servtab *
-getconfigent(void)
+ rlim_ofile_cur = rl.rlim_cur;
+}
+
+static void remove_fd_from_set(int fd)
{
- static struct servtab serv;
- struct servtab *sep = &serv;
- int argc;
- char *cp, *arg;
-
-more:
- while ((cp = nextline()) && *cp == '#')
- ;
- if (cp == NULL)
- return ((struct servtab *)0);
- memset((char *)sep, 0, sizeof *sep);
- sep->se_service = newstr(skip(&cp));
- arg = skip(&cp);
- if (arg == NULL)
- goto more;
-
- if (strcmp(arg, "stream") == 0)
- sep->se_socktype = SOCK_STREAM;
- else if (strcmp(arg, "dgram") == 0)
- sep->se_socktype = SOCK_DGRAM;
- else if (strcmp(arg, "rdm") == 0)
- sep->se_socktype = SOCK_RDM;
- else if (strcmp(arg, "seqpacket") == 0)
- sep->se_socktype = SOCK_SEQPACKET;
- else if (strcmp(arg, "raw") == 0)
- sep->se_socktype = SOCK_RAW;
- else
- sep->se_socktype = -1;
+ if (fd >= 0) {
+ FD_CLR(fd, &allsock);
+ maxsock = -1;
+ }
+}
- sep->se_proto = newstr(skip(&cp));
- if (strcmp(sep->se_proto, "unix") == 0) {
- sep->se_family = AF_UNIX;
- } else {
- sep->se_family = AF_INET;
- if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
- syslog(LOG_ERR, "%s: rpc services not suported",
- sep->se_service);
- goto more;
+static void add_fd_to_set(int fd)
+{
+ if (fd >= 0) {
+ FD_SET(fd, &allsock);
+ if (maxsock >= 0 && fd > maxsock) {
+ prev_maxsock = maxsock = fd;
+ if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN)
+ bump_nofile();
}
}
- arg = skip(&cp);
- if (arg == NULL)
- goto more;
- {
- char *s = strchr(arg, '.');
- if (s) {
- *s++ = '\0';
- sep->se_max = atoi(s);
- } else
- sep->se_max = TOOMANY;
+}
+
+static void recalculate_maxsock(void)
+{
+ int fd = 0;
+
+ /* We may have no services, in this case maxsock should still be >= 0
+ * (code elsewhere is not happy with maxsock == -1) */
+ maxsock = 0;
+ while (fd <= prev_maxsock) {
+ if (FD_ISSET(fd, &allsock))
+ maxsock = fd;
+ fd++;
}
- sep->se_wait = strcmp(arg, "wait") == 0;
- sep->se_user = newstr(skip(&cp));
- sep->se_group = strchr(sep->se_user, '.');
- if (sep->se_group) {
- *sep->se_group++ = '\0';
+ prev_maxsock = maxsock;
+ if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
+ bump_nofile();
+}
+
+static void prepare_socket_fd(servtab_t *sep)
+{
+ int r, fd;
+
+ fd = socket(sep->se_family, sep->se_socktype, 0);
+ if (fd < 0) {
+ bb_perror_msg("socket");
+ return;
}
- sep->se_server = newstr(skip(&cp));
- if (strcmp(sep->se_server, "internal") == 0) {
-#ifndef INETD_UNSUPPORT_BILTIN
- const struct biltin *bi;
-
- for (bi = biltins; bi->bi_service; bi++)
- if (bi->bi_socktype == sep->se_socktype &&
- strcmp(bi->bi_service, sep->se_service) == 0)
- break;
- if (bi->bi_service == 0) {
- syslog(LOG_ERR, "internal service %s unknown",
- sep->se_service);
- goto more;
+ setsockopt_reuseaddr(fd);
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (is_rpc_service(sep)) {
+ struct passwd *pwd;
+
+ /* zero out the port for all RPC services; let bind()
+ * find one. */
+ set_nport(sep->se_lsa, 0);
+
+ /* for RPC services, attempt to use a reserved port
+ * if they are going to be running as root. */
+ if (real_uid == 0 && sep->se_family == AF_INET
+ && (pwd = getpwnam(sep->se_user)) != NULL
+ && pwd->pw_uid == 0
+ ) {
+ r = bindresvport(fd, &sep->se_lsa->u.sin);
+ } else {
+ r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
+ }
+ if (r == 0) {
+ int saveerrno = errno;
+ /* update lsa with port# */
+ getsockname(fd, &sep->se_lsa->u.sa, &sep->se_lsa->len);
+ errno = saveerrno;
}
- sep->se_bi = bi;
- sep->se_wait = bi->bi_wait;
-#else
- syslog(LOG_ERR, "internal service %s unknown",
- sep->se_service);
- goto more;
-#endif
} else
-#ifndef INETD_UNSUPPORT_BILTIN
- sep->se_bi = NULL
#endif
- ;
- argc = 0;
- for (arg = skip(&cp); cp; arg = skip(&cp)) {
- if (argc < MAXARGV)
- sep->se_argv[argc++] = newstr(arg);
+ {
+ if (sep->se_family == AF_UNIX) {
+ struct sockaddr_un *sun;
+ sun = (struct sockaddr_un*)&(sep->se_lsa->u.sa);
+ unlink(sun->sun_path);
+ }
+ r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
}
- while (argc <= MAXARGV)
- sep->se_argv[argc++] = NULL;
- return (sep);
+ if (r < 0) {
+ bb_perror_msg("%s/%s: bind",
+ sep->se_service, sep->se_proto);
+ close(fd);
+ rearm_alarm();
+ return;
+ }
+ if (sep->se_socktype == SOCK_STREAM)
+ listen(fd, global_queuelen);
+
+ add_fd_to_set(fd);
+ sep->se_fd = fd;
}
-static void
-freeconfig(struct servtab *cp)
+static int reopen_config_file(void)
+{
+ free(default_local_hostname);
+ default_local_hostname = xstrdup("*");
+ if (parser != NULL)
+ config_close(parser);
+ parser = config_open(config_filename);
+ return (parser != NULL);
+}
+
+static void close_config_file(void)
+{
+ if (parser) {
+ config_close(parser);
+ parser = NULL;
+ }
+}
+
+static void free_servtab_strings(servtab_t *cp)
{
int i;
+ free(cp->se_local_hostname);
free(cp->se_service);
free(cp->se_proto);
free(cp->se_user);
- /* Note: se_group is part of the newstr'ed se_user */
- free(cp->se_server);
+ free(cp->se_group);
+ free(cp->se_lsa); /* not a string in fact */
+ free(cp->se_program);
for (i = 0; i < MAXARGV; i++)
free(cp->se_argv[i]);
}
-#ifndef INETD_UNSUPPORT_BILTIN
-static char **Argv;
-static char *LastArg;
-
-static void
-setproctitle(char *a, int s)
+static servtab_t *new_servtab(void)
{
- size_t size;
- char *cp;
- struct sockaddr_in sn;
- char buf[80];
-
- cp = Argv[0];
- size = sizeof(sn);
- if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
- (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
- else
- (void) sprintf(buf, "-%s", a);
- strncpy(cp, buf, LastArg - cp);
- cp += strlen(cp);
- while (cp < LastArg)
- *cp++ = ' ';
+ servtab_t *newtab = xzalloc(sizeof(servtab_t));
+ newtab->se_fd = -1; /* paranoia */
+ return newtab;
}
-#endif /* INETD_UNSUPPORT_BILTIN */
-static struct servtab *
-enter(struct servtab *cp)
+static servtab_t *dup_servtab(servtab_t *sep)
{
- struct servtab *sep;
- sigset_t oldmask;
+ servtab_t *newtab;
+ int argc;
- sep = (struct servtab *)malloc(sizeof (*sep));
- if (sep == NULL) {
- syslog_err_and_discard_dg(SOCK_STREAM, bb_msg_memory_exhausted);
- }
- *sep = *cp;
- sep->se_fd = -1;
- sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
- sep->se_next = servtab;
- servtab = sep;
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
- return (sep);
+ newtab = new_servtab();
+ *newtab = *sep; /* struct copy */
+ /* deep-copying strings */
+ newtab->se_service = xstrdup(newtab->se_service);
+ newtab->se_proto = xstrdup(newtab->se_proto);
+ newtab->se_user = xstrdup(newtab->se_user);
+ newtab->se_group = xstrdup(newtab->se_group);
+ newtab->se_program = xstrdup(newtab->se_program);
+ for (argc = 0; argc <= MAXARGV; argc++)
+ newtab->se_argv[argc] = xstrdup(newtab->se_argv[argc]);
+ /* NB: se_fd, se_hostaddr and se_next are always
+ * overwrittend by callers, so we don't bother resetting them
+ * to NULL/0/-1 etc */
+
+ return newtab;
}
-static int
-bump_nofile(void)
+/* gcc generates much more code if this is inlined */
+static servtab_t *parse_one_line(void)
{
-#ifdef RLIMIT_NOFILE
-
-#define FD_CHUNK 32
+ int argc;
+ char *token[6+MAXARGV];
+ char *p, *arg;
+ char *hostdelim;
+ servtab_t *sep;
+ servtab_t *nsep;
+ new:
+ sep = new_servtab();
+ more:
+ argc = config_read(parser, token, 6+MAXARGV, 1, "# \t", PARSE_NORMAL);
+ if (!argc) {
+ free(sep);
+ return NULL;
+ }
- struct rlimit rl;
+ /* [host:]service socktype proto wait user[:group] prog [args] */
+ /* Check for "host:...." line */
+ arg = token[0];
+ hostdelim = strrchr(arg, ':');
+ if (hostdelim) {
+ *hostdelim = '\0';
+ sep->se_local_hostname = xstrdup(arg);
+ arg = hostdelim + 1;
+ if (*arg == '\0' && argc == 1) {
+ /* Line has just "host:", change the
+ * default host for the following lines. */
+ free(default_local_hostname);
+ default_local_hostname = sep->se_local_hostname;
+ goto more;
+ }
+ } else
+ sep->se_local_hostname = xstrdup(default_local_hostname);
+
+ /* service socktype proto wait user[:group] prog [args] */
+ sep->se_service = xstrdup(arg);
+
+ /* socktype proto wait user[:group] prog [args] */
+ if (argc < 6) {
+ parse_err:
+ bb_error_msg("parse error on line %u, line is ignored",
+ parser->lineno);
+ free_servtab_strings(sep);
+ /* Just "goto more" can make sep to carry over e.g.
+ * "rpc"-ness (by having se_rpcver_lo != 0).
+ * We will be more paranoid: */
+ free(sep);
+ goto new;
+ }
- if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
- syslog(LOG_ERR, "getrlimit: %m");
- return -1;
+ {
+ static int8_t SOCK_xxx[] ALIGN1 = {
+ -1,
+ SOCK_STREAM, SOCK_DGRAM, SOCK_RDM,
+ SOCK_SEQPACKET, SOCK_RAW
+ };
+ sep->se_socktype = SOCK_xxx[1 + index_in_strings(
+ "stream""\0" "dgram""\0" "rdm""\0"
+ "seqpacket""\0" "raw""\0"
+ , token[1])];
}
- rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
- if (rl.rlim_cur <= rlim_ofile_cur) {
- syslog(LOG_ERR,
-#if _FILE_OFFSET_BITS == 64
- "bump_nofile: cannot extend file limit, max = %lld",
+
+ /* {unix,[rpc/]{tcp,udp}[6]} wait user[:group] prog [args] */
+ sep->se_proto = arg = xstrdup(token[2]);
+ if (strcmp(arg, "unix") == 0) {
+ sep->se_family = AF_UNIX;
+ } else {
+ char *six;
+ sep->se_family = AF_INET;
+ six = last_char_is(arg, '6');
+ if (six) {
+#if ENABLE_FEATURE_IPV6
+ *six = '\0';
+ sep->se_family = AF_INET6;
+#else
+ bb_error_msg("%s: no support for IPv6", sep->se_proto);
+ goto parse_err;
+#endif
+ }
+ if (strncmp(arg, "rpc/", 4) == 0) {
+#if ENABLE_FEATURE_INETD_RPC
+ unsigned n;
+ arg += 4;
+ p = strchr(sep->se_service, '/');
+ if (p == NULL) {
+ bb_error_msg("no rpc version: '%s'", sep->se_service);
+ goto parse_err;
+ }
+ *p++ = '\0';
+ n = bb_strtou(p, &p, 10);
+ if (n > INT_MAX) {
+ bad_ver_spec:
+ bb_error_msg("bad rpc version");
+ goto parse_err;
+ }
+ sep->se_rpcver_lo = sep->se_rpcver_hi = n;
+ if (*p == '-') {
+ p++;
+ n = bb_strtou(p, &p, 10);
+ if (n > INT_MAX || (int)n < sep->se_rpcver_lo)
+ goto bad_ver_spec;
+ sep->se_rpcver_hi = n;
+ }
+ if (*p != '\0')
+ goto bad_ver_spec;
#else
- "bump_nofile: cannot extend file limit, max = %ld",
+ bb_error_msg("no support for rpc services");
+ goto parse_err;
#endif
- rl.rlim_cur);
- return -1;
+ }
+ /* we don't really need getprotobyname()! */
+ if (strcmp(arg, "tcp") == 0)
+ sep->se_proto_no = IPPROTO_TCP; /* = 6 */
+ if (strcmp(arg, "udp") == 0)
+ sep->se_proto_no = IPPROTO_UDP; /* = 17 */
+ if (six)
+ *six = '6';
+ if (!sep->se_proto_no) /* not tcp/udp?? */
+ goto parse_err;
}
- if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
- syslog(LOG_ERR, "setrlimit: %m");
- return -1;
+ /* [no]wait[.max] user[:group] prog [args] */
+ arg = token[3];
+ sep->se_max = max_concurrency;
+ p = strchr(arg, '.');
+ if (p) {
+ *p++ = '\0';
+ sep->se_max = bb_strtou(p, NULL, 10);
+ if (errno)
+ goto parse_err;
+ }
+ sep->se_wait = (arg[0] != 'n' || arg[1] != 'o');
+ if (!sep->se_wait) /* "no" seen */
+ arg += 2;
+ if (strcmp(arg, "wait") != 0)
+ goto parse_err;
+
+ /* user[:group] prog [args] */
+ sep->se_user = xstrdup(token[4]);
+ arg = strchr(sep->se_user, '.');
+ if (arg == NULL)
+ arg = strchr(sep->se_user, ':');
+ if (arg) {
+ *arg++ = '\0';
+ sep->se_group = xstrdup(arg);
}
- rlim_ofile_cur = rl.rlim_cur;
- return 0;
-
-#else
- syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
- return -1;
+ /* prog [args] */
+ sep->se_program = xstrdup(token[5]);
+#ifdef INETD_BUILTINS_ENABLED
+ if (strcmp(sep->se_program, "internal") == 0
+ && strlen(sep->se_service) <= 7
+ && (sep->se_socktype == SOCK_STREAM
+ || sep->se_socktype == SOCK_DGRAM)
+ ) {
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE(builtins); i++)
+ if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0)
+ goto found_bi;
+ bb_error_msg("unknown internal service %s", sep->se_service);
+ goto parse_err;
+ found_bi:
+ sep->se_builtin = &builtins[i];
+ /* stream builtins must be "nowait", dgram must be "wait" */
+ if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM))
+ goto parse_err;
+ }
#endif
-}
-
-
-static void
-setup(struct servtab *sep)
-{
- int on = 1;
+ argc = 0;
+ while ((arg = token[6+argc]) != NULL && argc < MAXARGV)
+ sep->se_argv[argc++] = xstrdup(arg);
- if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
- syslog(LOG_ERR, "%s/%s: socket: %m",
- sep->se_service, sep->se_proto);
- return;
+ /* catch mixups. "<service> stream udp ..." == wtf */
+ if (sep->se_socktype == SOCK_STREAM) {
+ if (sep->se_proto_no == IPPROTO_UDP)
+ goto parse_err;
}
- if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
- sizeof(on)) < 0)
- syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
- if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
- syslog(LOG_ERR, "%s/%s: bind: %m",
- sep->se_service, sep->se_proto);
- (void) close(sep->se_fd);
- sep->se_fd = -1;
- if (!timingout) {
- timingout = 1;
- alarm(RETRYTIME);
- }
- return;
+ if (sep->se_socktype == SOCK_DGRAM) {
+ if (sep->se_proto_no == IPPROTO_TCP)
+ goto parse_err;
}
- if (sep->se_socktype == SOCK_STREAM)
- listen(sep->se_fd, global_queuelen);
-
- FD_SET(sep->se_fd, &allsock);
- nsock++;
- if (sep->se_fd > maxsock) {
- maxsock = sep->se_fd;
- if (maxsock > rlim_ofile_cur - FD_MARGIN)
- bump_nofile();
+
+// bb_info_msg(
+// "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]",
+// sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no,
+// sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program);
+
+ /* check if the hostname specifier is a comma separated list
+ * of hostnames. we'll make new entries for each address. */
+ while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) {
+ nsep = dup_servtab(sep);
+ /* NUL terminate the hostname field of the existing entry,
+ * and make a dup for the new entry. */
+ *hostdelim++ = '\0';
+ nsep->se_local_hostname = xstrdup(hostdelim);
+ nsep->se_next = sep->se_next;
+ sep->se_next = nsep;
}
+
+ /* was doing it here: */
+ /* DNS resolution, create copies for each IP address */
+ /* IPv6-ization destroyed it :( */
+
+ return sep;
}
-static void
-config(int signum)
+static servtab_t *insert_in_servlist(servtab_t *cp)
{
- struct servtab *sep, *cp, **sepp;
- sigset_t oldmask;
- unsigned n;
+ servtab_t *sep;
+ sigset_t omask;
- (void)signum;
- if (setconfig() == NULL)
- return;
+ sep = new_servtab();
+ *sep = *cp; /* struct copy */
+ sep->se_fd = -1;
+#if ENABLE_FEATURE_INETD_RPC
+ sep->se_rpcprog = -1;
+#endif
+ block_CHLD_HUP_ALRM(&omask);
+ sep->se_next = serv_list;
+ serv_list = sep;
+ restore_sigmask(&omask);
+ return sep;
+}
+
+static int same_serv_addr_proto(servtab_t *old, servtab_t *new)
+{
+ if (strcmp(old->se_local_hostname, new->se_local_hostname) != 0)
+ return 0;
+ if (strcmp(old->se_service, new->se_service) != 0)
+ return 0;
+ if (strcmp(old->se_proto, new->se_proto) != 0)
+ return 0;
+ return 1;
+}
+
+static void reread_config_file(int sig UNUSED_PARAM)
+{
+ servtab_t *sep, *cp, **sepp;
+ len_and_sockaddr *lsa;
+ sigset_t omask;
+ unsigned n;
+ uint16_t port;
+ int save_errno = errno;
- for (sep = servtab; sep; sep = sep->se_next)
+ if (!reopen_config_file())
+ goto ret;
+ for (sep = serv_list; sep; sep = sep->se_next)
sep->se_checked = 0;
- while ((cp = getconfigent()) != NULL) {
- for (sep = servtab; sep; sep = sep->se_next)
- if (strcmp(sep->se_service, cp->se_service) == 0 &&
- strcmp(sep->se_proto, cp->se_proto) == 0)
+
+ goto first_line;
+ while (1) {
+ if (cp == NULL) {
+ first_line:
+ cp = parse_one_line();
+ if (cp == NULL)
break;
- if (sep != 0) {
+ }
+ for (sep = serv_list; sep; sep = sep->se_next)
+ if (same_serv_addr_proto(sep, cp))
+ goto equal_servtab;
+ /* not an "equal" servtab */
+ sep = insert_in_servlist(cp);
+ goto after_check;
+ equal_servtab:
+ {
int i;
-#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
-
- sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
- /*
- * sep->se_wait may be holding the pid of a daemon
- * that we're waiting for. If so, don't overwrite
- * it unless the config file explicitly says don't
- * wait.
- */
- if (
-#ifndef INETD_UNSUPPORT_BILTIN
- cp->se_bi == 0 &&
+ block_CHLD_HUP_ALRM(&omask);
+#if ENABLE_FEATURE_INETD_RPC
+ if (is_rpc_service(sep))
+ unregister_rpc(sep);
+ sep->se_rpcver_lo = cp->se_rpcver_lo;
+ sep->se_rpcver_hi = cp->se_rpcver_hi;
#endif
- (sep->se_wait == 1 || cp->se_wait == 0))
- sep->se_wait = cp->se_wait;
- if (cp->se_max != sep->se_max)
- SWAP(int, cp->se_max, sep->se_max);
- if (cp->se_user)
- SWAP(char *, sep->se_user, cp->se_user);
- if (cp->se_group)
- SWAP(char *, sep->se_group, cp->se_group);
- if (cp->se_server)
- SWAP(char *, sep->se_server, cp->se_server);
+ if (cp->se_wait == 0) {
+ /* New config says "nowait". If old one
+ * was "wait", we currently may be waiting
+ * for a child (and not accepting connects).
+ * Stop waiting, start listening again.
+ * (if it's not true, this op is harmless) */
+ add_fd_to_set(sep->se_fd);
+ }
+ sep->se_wait = cp->se_wait;
+ sep->se_max = cp->se_max;
+ /* string fields need more love - we don't want to leak them */
+#define SWAP(type, a, b) do { type c = (type)a; a = (type)b; b = (type)c; } while (0)
+ SWAP(char*, sep->se_user, cp->se_user);
+ SWAP(char*, sep->se_group, cp->se_group);
+ SWAP(char*, sep->se_program, cp->se_program);
for (i = 0; i < MAXARGV; i++)
- SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
+ SWAP(char*, sep->se_argv[i], cp->se_argv[i]);
#undef SWAP
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
- freeconfig(cp);
- } else {
- sep = enter(cp);
+ restore_sigmask(&omask);
+ free_servtab_strings(cp);
}
+ after_check:
+ /* cp->string_fields are consumed by insert_in_servlist()
+ * or freed at this point, cp itself is not yet freed. */
sep->se_checked = 1;
+ /* create new len_and_sockaddr */
switch (sep->se_family) {
+ struct sockaddr_un *sun;
case AF_UNIX:
- if (sep->se_fd != -1)
- break;
- (void)unlink(sep->se_service);
- n = strlen(sep->se_service);
- if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
- n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
- strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
- sep->se_ctrladdr_un.sun_family = AF_UNIX;
- sep->se_ctrladdr_size = n +
- sizeof sep->se_ctrladdr_un.sun_family;
- setup(sep);
+ lsa = xzalloc_lsa(AF_UNIX);
+ sun = (struct sockaddr_un*)&lsa->u.sa;
+ safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path));
break;
- case AF_INET:
- sep->se_ctrladdr_in.sin_family = AF_INET;
- sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
- {
- u_short port = htons(atoi(sep->se_service));
-
- if (!port) {
- struct servent *sp;
- sp = getservbyname(sep->se_service,
- sep->se_proto);
- if (sp == 0) {
- syslog(LOG_ERR,
- "%s/%s: unknown service",
- sep->se_service, sep->se_proto);
- continue;
- }
- port = sp->s_port;
- }
- if (port != sep->se_ctrladdr_in.sin_port) {
- sep->se_ctrladdr_in.sin_port = port;
- if (sep->se_fd != -1) {
- FD_CLR(sep->se_fd, &allsock);
- nsock--;
- (void) close(sep->se_fd);
+
+ default: /* case AF_INET, case AF_INET6 */
+ n = bb_strtou(sep->se_service, NULL, 10);
+#if ENABLE_FEATURE_INETD_RPC
+ if (is_rpc_service(sep)) {
+ sep->se_rpcprog = n;
+ if (errno) { /* se_service is not numeric */
+ struct rpcent *rp = getrpcbyname(sep->se_service);
+ if (rp == NULL) {
+ bb_error_msg("%s: unknown rpc service", sep->se_service);
+ goto next_cp;
}
- sep->se_fd = -1;
+ sep->se_rpcprog = rp->r_number;
}
if (sep->se_fd == -1)
- setup(sep);
+ prepare_socket_fd(sep);
+ if (sep->se_fd != -1)
+ register_rpc(sep);
+ goto next_cp;
+ }
+#endif
+ /* what port to listen on? */
+ port = htons(n);
+ if (errno || n > 0xffff) { /* se_service is not numeric */
+ char protoname[4];
+ struct servent *sp;
+ /* can result only in "tcp" or "udp": */
+ safe_strncpy(protoname, sep->se_proto, 4);
+ sp = getservbyname(sep->se_service, protoname);
+ if (sp == NULL) {
+ bb_error_msg("%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ goto next_cp;
+ }
+ port = sp->s_port;
}
+ if (LONE_CHAR(sep->se_local_hostname, '*')) {
+ lsa = xzalloc_lsa(sep->se_family);
+ set_nport(lsa, port);
+ } else {
+ lsa = host_and_af2sockaddr(sep->se_local_hostname,
+ ntohs(port), sep->se_family);
+ if (!lsa) {
+ bb_error_msg("%s/%s: unknown host '%s'",
+ sep->se_service, sep->se_proto,
+ sep->se_local_hostname);
+ goto next_cp;
+ }
+ }
+ break;
+ } /* end of "switch (sep->se_family)" */
+
+ /* did lsa change? Then close/open */
+ if (sep->se_lsa == NULL
+ || lsa->len != sep->se_lsa->len
+ || memcmp(&lsa->u.sa, &sep->se_lsa->u.sa, lsa->len) != 0
+ ) {
+ remove_fd_from_set(sep->se_fd);
+ maybe_close(sep->se_fd);
+ free(sep->se_lsa);
+ sep->se_lsa = lsa;
+ sep->se_fd = -1;
+ } else {
+ free(lsa);
}
- }
- if (fconfig) {
- (void) fclose(fconfig);
- fconfig = NULL;
- }
- /*
- * Purge anything not looked at above.
- */
- sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
- sepp = &servtab;
- while ((sep = *sepp) != NULL) {
+ if (sep->se_fd == -1)
+ prepare_socket_fd(sep);
+ next_cp:
+ sep = cp->se_next;
+ free(cp);
+ cp = sep;
+ } /* end of "while (1) parse lines" */
+ close_config_file();
+
+ /* Purge anything not looked at above - these are stale entries,
+ * new config file doesnt have them. */
+ block_CHLD_HUP_ALRM(&omask);
+ sepp = &serv_list;
+ while ((sep = *sepp)) {
if (sep->se_checked) {
sepp = &sep->se_next;
continue;
}
*sepp = sep->se_next;
- if (sep->se_fd != -1) {
- FD_CLR(sep->se_fd, &allsock);
- nsock--;
- (void) close(sep->se_fd);
- }
+ remove_fd_from_set(sep->se_fd);
+ maybe_close(sep->se_fd);
+#if ENABLE_FEATURE_INETD_RPC
+ if (is_rpc_service(sep))
+ unregister_rpc(sep);
+#endif
if (sep->se_family == AF_UNIX)
- (void)unlink(sep->se_service);
- freeconfig(sep);
- free((char *)sep);
+ unlink(sep->se_service);
+ free_servtab_strings(sep);
+ free(sep);
}
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ restore_sigmask(&omask);
+ ret:
+ errno = save_errno;
}
-
-
-static void
-reapchild(int signum)
+static void reap_child(int sig UNUSED_PARAM)
{
+ pid_t pid;
int status;
- int pid;
- struct servtab *sep;
+ servtab_t *sep;
+ int save_errno = errno;
- (void)signum;
for (;;) {
- pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ pid = wait_any_nohang(&status);
if (pid <= 0)
break;
- for (sep = servtab; sep; sep = sep->se_next)
- if (sep->se_wait == pid) {
- if (WIFEXITED(status) && WEXITSTATUS(status))
- syslog(LOG_WARNING,
- "%s: exit status 0x%x",
- sep->se_server, WEXITSTATUS(status));
- else if (WIFSIGNALED(status))
- syslog(LOG_WARNING,
- "%s: exit signal 0x%x",
- sep->se_server, WTERMSIG(status));
- sep->se_wait = 1;
- FD_SET(sep->se_fd, &allsock);
- nsock++;
- }
+ for (sep = serv_list; sep; sep = sep->se_next) {
+ if (sep->se_wait != pid)
+ continue;
+ /* One of our "wait" services */
+ if (WIFEXITED(status) && WEXITSTATUS(status))
+ bb_error_msg("%s: exit status 0x%x",
+ sep->se_program, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ bb_error_msg("%s: exit signal 0x%x",
+ sep->se_program, WTERMSIG(status));
+ sep->se_wait = 1;
+ add_fd_to_set(sep->se_fd);
+ break;
+ }
}
+ errno = save_errno;
}
-static void
-retry(int signum)
+static void retry_network_setup(int sig UNUSED_PARAM)
{
- struct servtab *sep;
+ int save_errno = errno;
+ servtab_t *sep;
- (void)signum;
- timingout = 0;
- for (sep = servtab; sep; sep = sep->se_next) {
+ alarm_armed = 0;
+ for (sep = serv_list; sep; sep = sep->se_next) {
if (sep->se_fd == -1) {
- switch (sep->se_family) {
- case AF_UNIX:
- case AF_INET:
- setup(sep);
- break;
- }
+ prepare_socket_fd(sep);
+#if ENABLE_FEATURE_INETD_RPC
+ if (sep->se_fd != -1 && is_rpc_service(sep))
+ register_rpc(sep);
+#endif
}
}
+ errno = save_errno;
}
-static void
-goaway(int signum)
+static void clean_up_and_exit(int sig UNUSED_PARAM)
{
- struct servtab *sep;
-
- (void)signum;
- for (sep = servtab; sep; sep = sep->se_next)
- if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
- (void)unlink(sep->se_service);
- (void)unlink(_PATH_INETDPID);
- exit(0);
-}
+ servtab_t *sep;
+ /* XXX signal race walking sep list */
+ for (sep = serv_list; sep; sep = sep->se_next) {
+ if (sep->se_fd == -1)
+ continue;
+ switch (sep->se_family) {
+ case AF_UNIX:
+ unlink(sep->se_service);
+ break;
+ default: /* case AF_INET, AF_INET6 */
+#if ENABLE_FEATURE_INETD_RPC
+ if (sep->se_wait == 1 && is_rpc_service(sep))
+ unregister_rpc(sep); /* XXX signal race */
+#endif
+ break;
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(sep->se_fd);
+ }
+ remove_pidfile(_PATH_INETDPID);
+ exit(EXIT_SUCCESS);
+}
-extern int
-inetd_main(int argc, char *argv[])
+int inetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int inetd_main(int argc UNUSED_PARAM, char **argv)
{
- struct servtab *sep;
+ struct sigaction sa, saved_pipe_handler;
+ servtab_t *sep, *sep2;
struct passwd *pwd;
- struct group *grp = NULL;
- struct sigaction sa;
- int pid;
- unsigned long opt;
- char *sq;
- gid_t gid;
-
-#ifdef INETD_UNSUPPORT_BILTIN
-# define dofork 1
-#else
- int dofork;
- extern char **environ;
-#endif
-
- gid = getgid();
- setgroups(1, &gid);
+ struct group *grp = grp; /* for compiler */
+ int opt;
+ pid_t pid;
+ sigset_t omask;
-#ifndef INETD_UNSUPPORT_BILTIN
- Argv = argv;
- if (environ == 0 || *environ == 0)
- environ = argv;
- while (*environ)
- environ++;
- LastArg = environ[-1] + strlen(environ[-1]);
-#endif
+ INIT_G();
-#if defined(__uClinux__)
- opt = bb_getopt_ulflags(argc, argv, "q:f", &sq);
- if (!(opt & 2)) {
- daemon(0, 0);
- /* reexec for vfork() do continue parent */
- vfork_daemon_rexec(argc, argv, "-f");
- }
-#else
- opt = bb_getopt_ulflags(argc, argv, "q:", &sq);
- daemon(0, 0);
-#endif /* uClinux */
+ real_uid = getuid();
+ if (real_uid != 0) /* run by non-root user */
+ config_filename = NULL;
- if(opt & 1) {
- global_queuelen = atoi(optarg);
- if (global_queuelen < 8) global_queuelen=8;
- }
- argc -= optind;
+ opt_complementary = "R+:q+"; /* -q N, -R N */
+ opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
argv += optind;
-
- if (argc > 0)
- CONFIG = argv[0];
-
- openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
- {
- FILE *fp;
-
- if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
- fprintf(fp, "%u\n", getpid());
- (void)fclose(fp);
- }
+ //argc -= optind;
+ if (argv[0])
+ config_filename = argv[0];
+ if (config_filename == NULL)
+ bb_error_msg_and_die("non-root must specify config file");
+ if (!(opt & 2))
+ bb_daemonize_or_rexec(0, argv - optind);
+ else
+ bb_sanitize_stdio();
+ if (!(opt & 4)) {
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
}
-#ifdef RLIMIT_NOFILE
- if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
- syslog(LOG_ERR, "getrlimit: %m");
- } else {
- rlim_ofile_cur = rlim_ofile.rlim_cur;
- if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
- rlim_ofile_cur = OPEN_MAX;
+ if (real_uid == 0) {
+ /* run by root, ensure groups vector gets trashed */
+ gid_t gid = getgid();
+ setgroups(1, &gid);
}
-#endif
- config(0);
+ write_pidfile(_PATH_INETDPID);
- sigemptyset(&emptymask);
- sigemptyset(&blockmask);
- sigaddset(&blockmask, SIGCHLD);
- sigaddset(&blockmask, SIGHUP);
- sigaddset(&blockmask, SIGALRM);
+ /* never fails under Linux (except if you pass it bad arguments) */
+ getrlimit(RLIMIT_NOFILE, &rlim_ofile);
+ rlim_ofile_cur = rlim_ofile.rlim_cur;
+ if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
+ rlim_ofile_cur = OPEN_MAX;
memset(&sa, 0, sizeof(sa));
- sa.sa_mask = blockmask;
- sa.sa_handler = retry;
- sigaction(SIGALRM, &sa, NULL);
- sa.sa_handler = config;
- sigaction(SIGHUP, &sa, NULL);
- sa.sa_handler = reapchild;
- sigaction(SIGCHLD, &sa, NULL);
- sa.sa_handler = goaway;
- sigaction(SIGTERM, &sa, NULL);
- sa.sa_handler = goaway;
- sigaction(SIGINT, &sa, NULL);
+ /*sigemptyset(&sa.sa_mask); - memset did it */
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_handler = retry_network_setup;
+ sigaction_set(SIGALRM, &sa);
+ sa.sa_handler = reread_config_file;
+ sigaction_set(SIGHUP, &sa);
+ sa.sa_handler = reap_child;
+ sigaction_set(SIGCHLD, &sa);
+ sa.sa_handler = clean_up_and_exit;
+ sigaction_set(SIGTERM, &sa);
+ sa.sa_handler = clean_up_and_exit;
+ sigaction_set(SIGINT, &sa);
sa.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sa, NULL);
+ sigaction(SIGPIPE, &sa, &saved_pipe_handler);
- {
- /* space for daemons to overwrite environment for ps */
-#define DUMMYSIZE 100
- char dummy[DUMMYSIZE];
-
- (void)memset(dummy, 'x', DUMMYSIZE - 1);
- dummy[DUMMYSIZE - 1] = '\0';
-
- (void)setenv("inetd_dummy", dummy, 1);
- }
+ reread_config_file(SIGHUP); /* load config from file */
for (;;) {
- int n, ctrl;
- fd_set readable;
-
- if (nsock == 0) {
- sigprocmask(SIG_BLOCK, &blockmask, NULL);
- while (nsock == 0)
- sigsuspend(&emptymask);
- sigprocmask(SIG_SETMASK, &emptymask, NULL);
- }
- readable = allsock;
- if ((n = select(maxsock + 1, &readable, (fd_set *)0,
- (fd_set *)0, (struct timeval *)0)) <= 0) {
- if (n < 0 && errno != EINTR)
- syslog(LOG_WARNING, "select: %m");
- sleep(1);
- continue;
- }
- for (sep = servtab; n && sep; sep = sep->se_next)
- if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
- n--;
- if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
- /* Fixed AGC */
- fcntl(sep->se_fd, F_SETFL, O_NDELAY);
- /* --------- */
- ctrl = accept(sep->se_fd, NULL, NULL);
- fcntl(sep->se_fd, F_SETFL, 0);
- if (ctrl < 0) {
- if (errno == EINTR || errno == EWOULDBLOCK)
- continue;
- syslog(LOG_WARNING, "accept (for %s): %m",
- sep->se_service);
- continue;
+ int ready_fd_cnt;
+ int ctrl, accepted_fd, new_udp_fd;
+ fd_set readable;
+
+ if (maxsock < 0)
+ recalculate_maxsock();
+
+ readable = allsock; /* struct copy */
+ /* if there are no fds to wait on, we will block
+ * until signal wakes us up (maxsock == 0, but readable
+ * never contains fds 0 and 1...) */
+ ready_fd_cnt = select(maxsock + 1, &readable, NULL, NULL, NULL);
+ if (ready_fd_cnt < 0) {
+ if (errno != EINTR) {
+ bb_perror_msg("select");
+ sleep(1);
}
- } else
+ continue;
+ }
+
+ for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) {
+ if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
+ continue;
+
+ ready_fd_cnt--;
ctrl = sep->se_fd;
- sigprocmask(SIG_BLOCK, &blockmask, NULL);
- pid = 0;
-#ifndef INETD_UNSUPPORT_BILTIN
- dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
-#endif
- if (dofork) {
- if (sep->se_count++ == 0)
- (void)gettimeofday(&sep->se_time,
- (struct timezone *)0);
- else if (sep->se_count >= sep->se_max) {
- struct timeval now;
-
- (void)gettimeofday(&now, (struct timezone *)0);
- if (now.tv_sec - sep->se_time.tv_sec >
- CNT_INTVL) {
- sep->se_time = now;
- sep->se_count = 1;
- } else {
- syslog(LOG_ERR,
- "%s/%s server failing (looping), service terminated",
- sep->se_service, sep->se_proto);
- FD_CLR(sep->se_fd, &allsock);
- (void) close(sep->se_fd);
- sep->se_fd = -1;
- sep->se_count = 0;
- nsock--;
- sigprocmask(SIG_SETMASK, &emptymask,
- NULL);
- if (!timingout) {
- timingout = 1;
- alarm(RETRYTIME);
+ accepted_fd = -1;
+ new_udp_fd = -1;
+ if (!sep->se_wait) {
+ if (sep->se_socktype == SOCK_STREAM) {
+ ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
+ if (ctrl < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("accept (for %s)", sep->se_service);
+ continue;
+ }
+ }
+ /* "nowait" udp */
+ if (sep->se_socktype == SOCK_DGRAM
+ && sep->se_family != AF_UNIX
+ ) {
+/* How udp "nowait" works:
+ * child peeks at (received and buffered by kernel) UDP packet,
+ * performs connect() on the socket so that it is linked only
+ * to this peer. But this also affects parent, because descriptors
+ * are shared after fork() a-la dup(). When parent performs
+ * select(), it will see this descriptor connected to the peer (!)
+ * and still readable, will act on it and mess things up
+ * (can create many copies of same child, etc).
+ * Parent must create and use new socket instead. */
+ new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
+ if (new_udp_fd < 0) { /* error: eat packet, forget about it */
+ udp_err:
+ recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
+ continue;
+ }
+ setsockopt_reuseaddr(new_udp_fd);
+ /* TODO: better do bind after vfork in parent,
+ * so that we don't have two wildcard bound sockets
+ * even for a brief moment? */
+ if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
+ close(new_udp_fd);
+ goto udp_err;
}
- continue;
}
}
- pid = fork();
- }
- if (pid < 0) {
- syslog(LOG_ERR, "fork: %m");
- if (sep->se_socktype == SOCK_STREAM)
- close(ctrl);
- sigprocmask(SIG_SETMASK, &emptymask, NULL);
- sleep(1);
- continue;
- }
- if (pid && sep->se_wait) {
- sep->se_wait = pid;
- FD_CLR(sep->se_fd, &allsock);
- nsock--;
- }
- sigprocmask(SIG_SETMASK, &emptymask, NULL);
- if (pid == 0) {
-#ifndef INETD_UNSUPPORT_BILTIN
- if (sep->se_bi)
- (*sep->se_bi->bi_fn)(ctrl, sep);
- else
+
+ block_CHLD_HUP_ALRM(&omask);
+ pid = 0;
+#ifdef INETD_BUILTINS_ENABLED
+ /* do we need to fork? */
+ if (sep->se_builtin == NULL
+ || (sep->se_socktype == SOCK_STREAM
+ && sep->se_builtin->bi_fork))
+#endif
+ {
+ if (sep->se_max != 0) {
+ if (++sep->se_count == 1)
+ sep->se_time = monotonic_sec();
+ else if (sep->se_count >= sep->se_max) {
+ unsigned now = monotonic_sec();
+ /* did we accumulate se_max connects too quickly? */
+ if (now - sep->se_time <= CNT_INTERVAL) {
+ bb_error_msg("%s/%s: too many connections, pausing",
+ sep->se_service, sep->se_proto);
+ remove_fd_from_set(sep->se_fd);
+ close(sep->se_fd);
+ sep->se_fd = -1;
+ sep->se_count = 0;
+ rearm_alarm(); /* will revive it in RETRYTIME sec */
+ restore_sigmask(&omask);
+ maybe_close(accepted_fd);
+ continue; /* -> check next fd in fd set */
+ }
+ sep->se_count = 0;
+ }
+ }
+ /* on NOMMU, streamed chargen
+ * builtin wouldn't work, but it is
+ * not allowed on NOMMU (ifdefed out) */
+#ifdef INETD_BUILTINS_ENABLED
+ if (BB_MMU && sep->se_builtin)
+ pid = fork();
+ else
#endif
- {
- if ((pwd = getpwnam(sep->se_user)) == NULL) {
- syslog_err_and_discard_dg(
- sep->se_socktype,
- "getpwnam: %s: No such user",
- sep->se_user);
+ pid = vfork();
+
+ if (pid < 0) { /* fork error */
+ bb_perror_msg("fork");
+ sleep(1);
+ restore_sigmask(&omask);
+ maybe_close(accepted_fd);
+ continue; /* -> check next fd in fd set */
}
- if (sep->se_group &&
- (grp = getgrnam(sep->se_group)) == NULL) {
- syslog_err_and_discard_dg(
- sep->se_socktype,
- "getgrnam: %s: No such group",
- sep->se_group);
+ if (pid == 0)
+ pid--; /* -1: "we did fork and we are child" */
+ }
+ /* if pid == 0 here, we never forked */
+
+ if (pid > 0) { /* parent */
+ if (sep->se_wait) {
+ /* tcp wait: we passed listening socket to child,
+ * will wait for child to terminate */
+ sep->se_wait = pid;
+ remove_fd_from_set(sep->se_fd);
}
- /*
- * Ok. There are four cases here:
- * 1. nonroot user, no group specified
- * 2. nonroot user, some group specified
- * 3. root user, no group specified
- * 4. root user, some group specified
- * In cases 2 and 4 we setgid to the specified
- * group. In cases 1 and 2 we run initgroups
- * to run with the groups of the given user.
- * In case 4 we do setgroups to run with the
- * given group. In case 3 we do nothing.
- */
- if (pwd->pw_uid) {
- if (sep->se_group)
- pwd->pw_gid = grp->gr_gid;
- setgid((gid_t)pwd->pw_gid);
- initgroups(pwd->pw_name, pwd->pw_gid);
- setuid((uid_t)pwd->pw_uid);
- } else if (sep->se_group) {
- setgid((gid_t)grp->gr_gid);
- setgroups(1, &grp->gr_gid);
+ if (new_udp_fd >= 0) {
+ /* udp nowait: child connected the socket,
+ * we created and will use new, unconnected one */
+ xmove_fd(new_udp_fd, sep->se_fd);
}
- dup2(ctrl, 0);
- close(ctrl);
- dup2(0, 1);
- dup2(0, 2);
-#ifdef RLIMIT_NOFILE
- if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
- if (setrlimit(RLIMIT_NOFILE,
- &rlim_ofile) < 0)
- syslog(LOG_ERR,"setrlimit: %m");
+ restore_sigmask(&omask);
+ maybe_close(accepted_fd);
+ continue; /* -> check next fd in fd set */
+ }
+
+ /* we are either child or didn't vfork at all */
+#ifdef INETD_BUILTINS_ENABLED
+ if (sep->se_builtin) {
+ if (pid) { /* "pid" is -1: we did vfork */
+ close(sep->se_fd); /* listening socket */
+ logmode = LOGMODE_NONE; /* make xwrite etc silent */
}
+ restore_sigmask(&omask);
+ if (sep->se_socktype == SOCK_STREAM)
+ sep->se_builtin->bi_stream_fn(ctrl, sep);
+ else
+ sep->se_builtin->bi_dgram_fn(ctrl, sep);
+ if (pid) /* we did vfork */
+ _exit(EXIT_FAILURE);
+ maybe_close(accepted_fd);
+ continue; /* -> check next fd in fd set */
+ }
#endif
- for (ctrl = rlim_ofile_cur-1; --ctrl > 2; )
- (void)close(ctrl);
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigaction(SIGPIPE, &sa, NULL);
-
- execv(sep->se_server, sep->se_argv);
- syslog_err_and_discard_dg(sep->se_socktype,
- "execv %s: %m", sep->se_server);
+ /* child */
+ setsid();
+ /* "nowait" udp */
+ if (new_udp_fd >= 0) {
+ len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family);
+ /* peek at the packet and remember peer addr */
+ int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
+ &lsa->u.sa, &lsa->len);
+ if (r < 0)
+ goto do_exit1;
+ /* make this socket "connected" to peer addr:
+ * only packets from this peer will be recv'ed,
+ * and bare write()/send() will work on it */
+ connect(ctrl, &lsa->u.sa, lsa->len);
+ free(lsa);
}
- }
- if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
- close(ctrl);
- }
- }
+ /* prepare env and exec program */
+ pwd = getpwnam(sep->se_user);
+ if (pwd == NULL) {
+ bb_error_msg("%s: no such %s", sep->se_user, "user");
+ goto do_exit1;
+ }
+ if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
+ bb_error_msg("%s: no such %s", sep->se_group, "group");
+ goto do_exit1;
+ }
+ if (real_uid != 0 && real_uid != pwd->pw_uid) {
+ /* a user running private inetd */
+ bb_error_msg("non-root must run services as himself");
+ goto do_exit1;
+ }
+ if (pwd->pw_uid) {
+ if (sep->se_group)
+ pwd->pw_gid = grp->gr_gid;
+ /* initgroups, setgid, setuid: */
+ change_identity(pwd);
+ } else if (sep->se_group) {
+ xsetgid(grp->gr_gid);
+ setgroups(1, &grp->gr_gid);
+ }
+ if (rlim_ofile.rlim_cur != rlim_ofile_cur)
+ if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
+ bb_perror_msg("setrlimit");
+ closelog();
+ xmove_fd(ctrl, 0);
+ xdup2(0, 1);
+ xdup2(0, 2);
+ /* NB: among others, this loop closes listening socket
+ * for nowait stream children */
+ for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
+ maybe_close(sep2->se_fd);
+ sigaction_set(SIGPIPE, &saved_pipe_handler);
+ restore_sigmask(&omask);
+ BB_EXECVP(sep->se_program, sep->se_argv);
+ bb_perror_msg("exec %s", sep->se_program);
+ do_exit1:
+ /* eat packet in udp case */
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, line, LINE_SIZE, MSG_DONTWAIT);
+ _exit(EXIT_FAILURE);
+ } /* for (sep = servtab...) */
+ } /* for (;;) */
}
+#if !BB_MMU
+static const char *const cat_args[] = { "cat", NULL };
+#endif
/*
* Internet services provided internally by inetd:
*/
-#define BUFSIZE 4096
-
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
-/* Echo service -- echo data back */
-static void
-echo_stream(int s, struct servtab *sep)
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+/* Echo service -- echo data back. */
+/* ARGSUSED */
+static void echo_stream(int s, servtab_t *sep UNUSED_PARAM)
{
- char buffer[BUFSIZE];
- int i;
-
- setproctitle(sep->se_service, s);
- while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
- write(s, buffer, i) > 0)
- ;
- exit(0);
+#if BB_MMU
+ while (1) {
+ ssize_t sz = safe_read(s, line, LINE_SIZE);
+ if (sz <= 0)
+ break;
+ xwrite(s, line, sz);
+ }
+#else
+ /* We are after vfork here! */
+ /* move network socket to stdin/stdout */
+ xmove_fd(s, STDIN_FILENO);
+ xdup2(STDIN_FILENO, STDOUT_FILENO);
+ /* no error messages please... */
+ close(STDERR_FILENO);
+ xopen(bb_dev_null, O_WRONLY);
+ BB_EXECVP("cat", (char**)cat_args);
+ /* on failure we return to main, which does exit(EXIT_FAILURE) */
+#endif
}
-
-/* Echo service -- echo data back */
-static void
-echo_dg(int s, struct servtab *sep)
+static void echo_dg(int s, servtab_t *sep)
{
- char buffer[BUFSIZE];
- int i;
- size_t size;
- struct sockaddr sa;
-
- (void)sep;
-
- size = sizeof(sa);
- if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
- return;
- (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
+ enum { BUFSIZE = 12*1024 }; /* for jumbo sized packets! :) */
+ char *buf = xmalloc(BUFSIZE); /* too big for stack */
+ int sz;
+ len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
+
+ lsa->len = sep->se_lsa->len;
+ /* dgram builtins are non-forking - DONT BLOCK! */
+ sz = recvfrom(s, buf, BUFSIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len);
+ if (sz > 0)
+ sendto(s, buf, sz, 0, &lsa->u.sa, lsa->len);
+ free(buf);
}
-#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
-/* Discard service -- ignore data */
-static void
-discard_stream(int s, struct servtab *sep)
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+/* Discard service -- ignore data. */
+/* ARGSUSED */
+static void discard_stream(int s, servtab_t *sep UNUSED_PARAM)
{
- char buffer[BUFSIZE];
-
- setproctitle(sep->se_service, s);
- while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
- errno == EINTR)
- ;
- exit(0);
+#if BB_MMU
+ while (safe_read(s, line, LINE_SIZE) > 0)
+ continue;
+#else
+ /* We are after vfork here! */
+ /* move network socket to stdin */
+ xmove_fd(s, STDIN_FILENO);
+ /* discard output */
+ close(STDOUT_FILENO);
+ xopen(bb_dev_null, O_WRONLY);
+ /* no error messages please... */
+ xdup2(STDOUT_FILENO, STDERR_FILENO);
+ BB_EXECVP("cat", (char**)cat_args);
+ /* on failure we return to main, which does exit(EXIT_FAILURE) */
+#endif
}
-
-/* Discard service -- ignore data */
-static void
-discard_dg(int s, struct servtab *sep)
+/* ARGSUSED */
+static void discard_dg(int s, servtab_t *sep UNUSED_PARAM)
{
- char buffer[BUFSIZE];
- (void)sep;
- read(s, buffer, sizeof(buffer));
+ /* dgram builtins are non-forking - DONT BLOCK! */
+ recv(s, line, LINE_SIZE, MSG_DONTWAIT);
}
-#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
-#include <ctype.h>
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
#define LINESIZ 72
-static char ring[128];
-static char *endring;
-
-static void
-initring(void)
+static void init_ring(void)
{
int i;
- endring = ring;
-
+ end_ring = ring;
for (i = 0; i <= 128; ++i)
if (isprint(i))
- *endring++ = i;
+ *end_ring++ = i;
}
-
-/* Character generator */
-static void
-chargen_stream(int s, struct servtab *sep)
+/* Character generator. MMU arches only. */
+/* ARGSUSED */
+static void chargen_stream(int s, servtab_t *sep UNUSED_PARAM)
{
char *rs;
int len;
- char text[LINESIZ+2];
-
- setproctitle(sep->se_service, s);
+ char text[LINESIZ + 2];
- if (!endring) {
- initring();
+ if (!end_ring) {
+ init_ring();
rs = ring;
}
text[LINESIZ] = '\r';
text[LINESIZ + 1] = '\n';
- for (rs = ring;;) {
- if ((len = endring - rs) >= LINESIZ)
- BCOPY(rs, text, LINESIZ);
+ rs = ring;
+ for (;;) {
+ len = end_ring - rs;
+ if (len >= LINESIZ)
+ memmove(text, rs, LINESIZ);
else {
- BCOPY(rs, text, len);
- BCOPY(ring, text + len, LINESIZ - len);
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
}
- if (++rs == endring)
+ if (++rs == end_ring)
rs = ring;
- if (write(s, text, sizeof(text)) != sizeof(text))
- break;
+ xwrite(s, text, sizeof(text));
}
- exit(0);
}
-
-/* Character generator */
-static void
-chargen_dg(int s, struct servtab *sep)
+/* ARGSUSED */
+static void chargen_dg(int s, servtab_t *sep)
{
- struct sockaddr sa;
- static char *rs;
- size_t len, size;
- char text[LINESIZ+2];
+ int len;
+ char text[LINESIZ + 2];
+ len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
- (void)sep;
+ /* Eat UDP packet which started it all */
+ /* dgram builtins are non-forking - DONT BLOCK! */
+ lsa->len = sep->se_lsa->len;
+ if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
+ return;
- if (endring == 0) {
- initring();
- rs = ring;
+ if (!end_ring) {
+ init_ring();
+ ring_pos = ring;
}
- size = sizeof(sa);
- if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
- return;
-
- if ((len = endring - rs) >= LINESIZ)
- BCOPY(rs, text, LINESIZ);
+ len = end_ring - ring_pos;
+ if (len >= LINESIZ)
+ memmove(text, ring_pos, LINESIZ);
else {
- BCOPY(rs, text, len);
- BCOPY(ring, text + len, LINESIZ - len);
+ memmove(text, ring_pos, len);
+ memmove(text + len, ring, LINESIZ - len);
}
- if (++rs == endring)
- rs = ring;
+ if (++ring_pos == end_ring)
+ ring_pos = ring;
text[LINESIZ] = '\r';
text[LINESIZ + 1] = '\n';
- (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
+ sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len);
}
-#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
/*
* Return a machine readable date and time, in the form of the
* number of seconds since midnight, Jan 1, 1900. Since gettimeofday
@@ -1222,81 +1536,57 @@ chargen_dg(int s, struct servtab *sep)
* we must add 2208988800 seconds to this figure to make up for
* some seventy years Bell Labs was asleep.
*/
-
-static long
-machtime(void)
+static uint32_t machtime(void)
{
struct timeval tv;
- if (gettimeofday(&tv, (struct timezone *)0) < 0) {
- fprintf(stderr, "Unable to get time of day\n");
- return (0L);
- }
- return (htonl((long)tv.tv_sec + 2208988800UL));
+ gettimeofday(&tv, NULL);
+ return htonl((uint32_t)(tv.tv_sec + 2208988800));
}
-
-static void
-machtime_stream(int s, struct servtab *sep)
+/* ARGSUSED */
+static void machtime_stream(int s, servtab_t *sep UNUSED_PARAM)
{
- long result;
- (void)sep;
+ uint32_t result;
result = machtime();
- write(s, (char *) &result, sizeof(result));
+ full_write(s, &result, sizeof(result));
}
-
-static void
-machtime_dg(int s, struct servtab *sep)
+static void machtime_dg(int s, servtab_t *sep)
{
- long result;
- struct sockaddr sa;
- size_t size;
- (void)sep;
+ uint32_t result;
+ len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
- size = sizeof(sa);
- if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
+ lsa->len = sep->se_lsa->len;
+ if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
return;
+
result = machtime();
- (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
+ sendto(s, &result, sizeof(result), 0, &lsa->u.sa, lsa->len);
}
-#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
-#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
/* Return human-readable time of day */
-static int
-human_readable_time_sprintf(char *buffer)
+/* ARGSUSED */
+static void daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
{
- time_t clocc = time(NULL);
+ time_t t;
- return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
+ t = time(NULL);
+ fdprintf(s, "%.24s\r\n", ctime(&t));
}
-
-static void
-daytime_stream(int s, struct servtab *sep)
+static void daytime_dg(int s, servtab_t *sep)
{
- char buffer[256];
- size_t st = human_readable_time_sprintf(buffer);
-
- (void)sep;
+ time_t t;
+ len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
- write(s, buffer, st);
-}
-
-/* Return human-readable time of day */
-static void
-daytime_dg(int s, struct servtab *sep)
-{
- char buffer[256];
- struct sockaddr sa;
- size_t size;
-
- (void)sep;
-
- size = sizeof(sa);
- if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
+ lsa->len = sep->se_lsa->len;
+ if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
return;
- size = human_readable_time_sprintf(buffer);
- sendto(s, buffer, size, 0, &sa, sizeof(sa));
+
+ t = time(NULL);
+ sprintf(line, "%.24s\r\n", ctime(&t));
+ sendto(s, line, strlen(line), 0, &lsa->u.sa, lsa->len);
}
-#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */
diff --git a/release/src/router/busybox/networking/interface.c b/release/src/router/busybox/networking/interface.c
new file mode 100644
index 00000000..a4b58ec6
--- /dev/null
+++ b/release/src/router/busybox/networking/interface.c
@@ -0,0 +1,1292 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ * Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+ * Added print_bytes_scaled function to reduce code size.
+ * Added some (potentially) missing defines.
+ * Improved display support for -a and for a named interface.
+ *
+ * -----------------------------------------------------------
+ *
+ * ifconfig This file contains an implementation of the command
+ * that either displays or sets the characteristics of
+ * one or more of the system's networking interfaces.
+ *
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * and others. Copyright 1993 MicroWalt Corporation
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Patched to support 'add' and 'del' keywords for INET(4) addresses
+ * by Mrs. Brisby <mrs.brisby@nimh.org>
+ *
+ * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - gettext instead of catgets for i18n
+ * 10/1998 - Andi Kleen. Use interface list primitives.
+ * 20001008 - Bernd Eckenfels, Patch from RH for setting mtu
+ * (default AF was wrong)
+ */
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include "inet_common.h"
+#include "libbb.h"
+
+
+#if ENABLE_FEATURE_HWIB
+/* #include <linux/if_infiniband.h> */
+#undef INFINIBAND_ALEN
+#define INFINIBAND_ALEN 20
+#endif
+
+#if ENABLE_FEATURE_IPV6
+# define HAVE_AFINET6 1
+#else
+# undef HAVE_AFINET6
+#endif
+
+#define _PATH_PROCNET_DEV "/proc/net/dev"
+#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
+
+#ifdef HAVE_AFINET6
+
+#ifndef _LINUX_IN6_H
+/*
+ * This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ uint32_t ifr6_prefixlen;
+ unsigned int ifr6_ifindex;
+};
+
+#endif
+
+#endif /* HAVE_AFINET6 */
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+#define SIOCSIFTXQLEN 0x8943
+#define SIOCGIFTXQLEN 0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+#define ifr_qlen ifr_ifru.ifru_mtu
+#endif
+
+#ifndef HAVE_TXQUEUELEN
+#define HAVE_TXQUEUELEN 1
+#endif
+
+#ifndef IFF_DYNAMIC
+#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
+#endif
+
+/* Display an Internet socket address. */
+static const char* FAST_FUNC INET_sprint(struct sockaddr *sap, int numeric)
+{
+ static char *buff; /* defaults to NULL */
+
+ free(buff);
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00);
+ return buff;
+}
+
+#ifdef UNUSED_AND_BUGGY
+static int INET_getsock(char *bufp, struct sockaddr *sap)
+{
+ char *sp = bufp, *bp;
+ unsigned int i;
+ unsigned val;
+ struct sockaddr_in *sock_in;
+
+ sock_in = (struct sockaddr_in *) sap;
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_port = 0;
+
+ val = 0;
+ bp = (char *) &val;
+ for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
+ *sp = toupper(*sp);
+
+ if ((unsigned)(*sp - 'A') <= 5)
+ bp[i] |= (int) (*sp - ('A' - 10));
+ else if (isdigit(*sp))
+ bp[i] |= (int) (*sp - '0');
+ else
+ return -1;
+
+ bp[i] <<= 4;
+ sp++;
+ *sp = toupper(*sp);
+
+ if ((unsigned)(*sp - 'A') <= 5)
+ bp[i] |= (int) (*sp - ('A' - 10));
+ else if (isdigit(*sp))
+ bp[i] |= (int) (*sp - '0');
+ else
+ return -1;
+
+ sp++;
+ }
+ sock_in->sin_addr.s_addr = htonl(val);
+
+ return (sp - bufp);
+}
+#endif
+
+static int FAST_FUNC INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+ return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
+/*
+ switch (type) {
+ case 1:
+ return (INET_getsock(bufp, sap));
+ case 256:
+ return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
+ default:
+ return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
+ }
+*/
+}
+
+static const struct aftype inet_aftype = {
+ .name = "inet",
+ .title = "DARPA Internet",
+ .af = AF_INET,
+ .alen = 4,
+ .sprint = INET_sprint,
+ .input = INET_input,
+};
+
+#ifdef HAVE_AFINET6
+
+/* Display an Internet socket address. */
+/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
+static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric)
+{
+ static char *buff;
+
+ free(buff);
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric);
+ return buff;
+}
+
+#ifdef UNUSED
+static int INET6_getsock(char *bufp, struct sockaddr *sap)
+{
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *) sap;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+
+ if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
+ return -1;
+
+ return 16; /* ?;) */
+}
+#endif
+
+static int FAST_FUNC INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+ return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
+/*
+ switch (type) {
+ case 1:
+ return (INET6_getsock(bufp, sap));
+ default:
+ return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
+ }
+*/
+}
+
+static const struct aftype inet6_aftype = {
+ .name = "inet6",
+ .title = "IPv6",
+ .af = AF_INET6,
+ .alen = sizeof(struct in6_addr),
+ .sprint = INET6_sprint,
+ .input = INET6_input,
+};
+
+#endif /* HAVE_AFINET6 */
+
+/* Display an UNSPEC address. */
+static char* FAST_FUNC UNSPEC_print(unsigned char *ptr)
+{
+ static char *buff;
+
+ char *pos;
+ unsigned int i;
+
+ if (!buff)
+ buff = xmalloc(sizeof(struct sockaddr) * 3 + 1);
+ pos = buff;
+ for (i = 0; i < sizeof(struct sockaddr); i++) {
+ /* careful -- not every libc's sprintf returns # bytes written */
+ sprintf(pos, "%02X-", (*ptr++ & 0377));
+ pos += 3;
+ }
+ /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */
+ *--pos = '\0';
+ return buff;
+}
+
+/* Display an UNSPEC socket address. */
+static const char* FAST_FUNC UNSPEC_sprint(struct sockaddr *sap, int numeric UNUSED_PARAM)
+{
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ return UNSPEC_print((unsigned char *)sap->sa_data);
+}
+
+static const struct aftype unspec_aftype = {
+ .name = "unspec",
+ .title = "UNSPEC",
+ .af = AF_UNSPEC,
+ .alen = 0,
+ .print = UNSPEC_print,
+ .sprint = UNSPEC_sprint,
+};
+
+static const struct aftype *const aftypes[] = {
+ &inet_aftype,
+#ifdef HAVE_AFINET6
+ &inet6_aftype,
+#endif
+ &unspec_aftype,
+ NULL
+};
+
+/* Check our protocol family table for this family. */
+const struct aftype* FAST_FUNC get_aftype(const char *name)
+{
+ const struct aftype *const *afp;
+
+ afp = aftypes;
+ while (*afp != NULL) {
+ if (!strcmp((*afp)->name, name))
+ return (*afp);
+ afp++;
+ }
+ return NULL;
+}
+
+/* Check our protocol family table for this family. */
+static const struct aftype *get_afntype(int af)
+{
+ const struct aftype *const *afp;
+
+ afp = aftypes;
+ while (*afp != NULL) {
+ if ((*afp)->af == af)
+ return *afp;
+ afp++;
+ }
+ return NULL;
+}
+
+struct user_net_device_stats {
+ unsigned long long rx_packets; /* total packets received */
+ unsigned long long tx_packets; /* total packets transmitted */
+ unsigned long long rx_bytes; /* total bytes received */
+ unsigned long long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* no space in linux buffers */
+ unsigned long tx_dropped; /* no space available in linux */
+ unsigned long rx_multicast; /* multicast packets received */
+ unsigned long rx_compressed;
+ unsigned long tx_compressed;
+ unsigned long collisions;
+
+ /* detailed rx_errors: */
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors; /* receiver ring buff overflow */
+ unsigned long rx_crc_errors; /* recved pkt with crc error */
+ unsigned long rx_frame_errors; /* recv'd frame alignment error */
+ unsigned long rx_fifo_errors; /* recv'r fifo overrun */
+ unsigned long rx_missed_errors; /* receiver missed packet */
+ /* detailed tx_errors */
+ unsigned long tx_aborted_errors;
+ unsigned long tx_carrier_errors;
+ unsigned long tx_fifo_errors;
+ unsigned long tx_heartbeat_errors;
+ unsigned long tx_window_errors;
+};
+
+struct interface {
+ struct interface *next, *prev;
+ char name[IFNAMSIZ]; /* interface name */
+ short type; /* if type */
+ short flags; /* various flags */
+ int metric; /* routing metric */
+ int mtu; /* MTU value */
+ int tx_queue_len; /* transmit queue length */
+ struct ifmap map; /* hardware setup */
+ struct sockaddr addr; /* IP address */
+ struct sockaddr dstaddr; /* P-P IP address */
+ struct sockaddr broadaddr; /* IP broadcast address */
+ struct sockaddr netmask; /* IP network mask */
+ int has_ip;
+ char hwaddr[32]; /* HW address */
+ int statistics_valid;
+ struct user_net_device_stats stats; /* statistics */
+ int keepalive; /* keepalive value for SLIP */
+ int outfill; /* outfill value for SLIP */
+};
+
+
+smallint interface_opt_a; /* show all interfaces */
+
+static struct interface *int_list, *int_last;
+
+
+#if 0
+/* like strcmp(), but knows about numbers */
+except that the freshly added calls to xatoul() brf on ethernet aliases with
+uClibc with e.g.: ife->name='lo' name='eth0:1'
+static int nstrcmp(const char *a, const char *b)
+{
+ const char *a_ptr = a;
+ const char *b_ptr = b;
+
+ while (*a == *b) {
+ if (*a == '\0') {
+ return 0;
+ }
+ if (!isdigit(*a) && isdigit(*(a+1))) {
+ a_ptr = a+1;
+ b_ptr = b+1;
+ }
+ a++;
+ b++;
+ }
+
+ if (isdigit(*a) && isdigit(*b)) {
+ return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
+ }
+ return *a - *b;
+}
+#endif
+
+static struct interface *add_interface(char *name)
+{
+ struct interface *ife, **nextp, *new;
+
+ for (ife = int_last; ife; ife = ife->prev) {
+ int n = /*n*/strcmp(ife->name, name);
+
+ if (n == 0)
+ return ife;
+ if (n < 0)
+ break;
+ }
+
+ new = xzalloc(sizeof(*new));
+ strncpy_IFNAMSIZ(new->name, name);
+ nextp = ife ? &ife->next : &int_list;
+ new->prev = ife;
+ new->next = *nextp;
+ if (new->next)
+ new->next->prev = new;
+ else
+ int_last = new;
+ *nextp = new;
+ return new;
+}
+
+static char *get_name(char *name, char *p)
+{
+ /* Extract <name> from nul-terminated p where p matches
+ <name>: after leading whitespace.
+ If match is not made, set name empty and return unchanged p */
+ int namestart = 0, nameend = 0;
+
+ while (isspace(p[namestart]))
+ namestart++;
+ nameend = namestart;
+ while (p[nameend] && p[nameend] != ':' && !isspace(p[nameend]))
+ nameend++;
+ if (p[nameend] == ':') {
+ if ((nameend - namestart) < IFNAMSIZ) {
+ memcpy(name, &p[namestart], nameend - namestart);
+ name[nameend - namestart] = '\0';
+ p = &p[nameend];
+ } else {
+ /* Interface name too large */
+ name[0] = '\0';
+ }
+ } else {
+ /* trailing ':' not found - return empty */
+ name[0] = '\0';
+ }
+ return p + 1;
+}
+
+/* If scanf supports size qualifiers for %n conversions, then we can
+ * use a modified fmt that simply stores the position in the fields
+ * having no associated fields in the proc string. Of course, we need
+ * to zero them again when we're done. But that is smaller than the
+ * old approach of multiple scanf occurrences with large numbers of
+ * args. */
+
+/* static const char *const ss_fmt[] = { */
+/* "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
+/* "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
+/* "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
+/* }; */
+
+ /* Lie about the size of the int pointed to for %n. */
+#if INT_MAX == LONG_MAX
+static const char *const ss_fmt[] = {
+ "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
+ "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
+ "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
+};
+#else
+static const char *const ss_fmt[] = {
+ "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
+ "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
+ "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
+};
+
+#endif
+
+static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
+{
+ memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
+
+ sscanf(bp, ss_fmt[procnetdev_vsn],
+ &ife->stats.rx_bytes, /* missing for 0 */
+ &ife->stats.rx_packets,
+ &ife->stats.rx_errors,
+ &ife->stats.rx_dropped,
+ &ife->stats.rx_fifo_errors,
+ &ife->stats.rx_frame_errors,
+ &ife->stats.rx_compressed, /* missing for <= 1 */
+ &ife->stats.rx_multicast, /* missing for <= 1 */
+ &ife->stats.tx_bytes, /* missing for 0 */
+ &ife->stats.tx_packets,
+ &ife->stats.tx_errors,
+ &ife->stats.tx_dropped,
+ &ife->stats.tx_fifo_errors,
+ &ife->stats.collisions,
+ &ife->stats.tx_carrier_errors,
+ &ife->stats.tx_compressed /* missing for <= 1 */
+ );
+
+ if (procnetdev_vsn <= 1) {
+ if (procnetdev_vsn == 0) {
+ ife->stats.rx_bytes = 0;
+ ife->stats.tx_bytes = 0;
+ }
+ ife->stats.rx_multicast = 0;
+ ife->stats.rx_compressed = 0;
+ ife->stats.tx_compressed = 0;
+ }
+}
+
+static int procnetdev_version(char *buf)
+{
+ if (strstr(buf, "compressed"))
+ return 2;
+ if (strstr(buf, "bytes"))
+ return 1;
+ return 0;
+}
+
+static int if_readconf(void)
+{
+ int numreqs = 30;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ int n, err = -1;
+ int skfd;
+
+ ifc.ifc_buf = NULL;
+
+ /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
+ (as of 2.1.128) */
+ skfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ bb_perror_msg("error: no inet socket available");
+ return -1;
+ }
+
+ for (;;) {
+ ifc.ifc_len = sizeof(struct ifreq) * numreqs;
+ ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
+
+ if (ioctl_or_warn(skfd, SIOCGIFCONF, &ifc) < 0) {
+ goto out;
+ }
+ if (ifc.ifc_len == (int)(sizeof(struct ifreq) * numreqs)) {
+ /* assume it overflowed and try again */
+ numreqs += 10;
+ continue;
+ }
+ break;
+ }
+
+ ifr = ifc.ifc_req;
+ for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
+ add_interface(ifr->ifr_name);
+ ifr++;
+ }
+ err = 0;
+
+ out:
+ close(skfd);
+ free(ifc.ifc_buf);
+ return err;
+}
+
+static int if_readlist_proc(char *target)
+{
+ static smallint proc_read;
+
+ FILE *fh;
+ char buf[512];
+ struct interface *ife;
+ int err, procnetdev_vsn;
+
+ if (proc_read)
+ return 0;
+ if (!target)
+ proc_read = 1;
+
+ fh = fopen_or_warn(_PATH_PROCNET_DEV, "r");
+ if (!fh) {
+ return if_readconf();
+ }
+ fgets(buf, sizeof buf, fh); /* eat line */
+ fgets(buf, sizeof buf, fh);
+
+ procnetdev_vsn = procnetdev_version(buf);
+
+ err = 0;
+ while (fgets(buf, sizeof buf, fh)) {
+ char *s, name[128];
+
+ s = get_name(name, buf);
+ ife = add_interface(name);
+ get_dev_fields(s, ife, procnetdev_vsn);
+ ife->statistics_valid = 1;
+ if (target && !strcmp(target, name))
+ break;
+ }
+ if (ferror(fh)) {
+ bb_perror_msg(_PATH_PROCNET_DEV);
+ err = -1;
+ proc_read = 0;
+ }
+ fclose(fh);
+ return err;
+}
+
+static int if_readlist(void)
+{
+ int err = if_readlist_proc(NULL);
+ /* Needed in order to get ethN:M aliases */
+ if (!err)
+ err = if_readconf();
+ return err;
+}
+
+/* Fetch the interface configuration from the kernel. */
+static int if_fetch(struct interface *ife)
+{
+ struct ifreq ifr;
+ char *ifname = ife->name;
+ int skfd;
+
+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
+ close(skfd);
+ return -1;
+ }
+ ife->flags = ifr.ifr_flags;
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ memset(ife->hwaddr, 0, 32);
+ if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
+ memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
+
+ ife->type = ifr.ifr_hwaddr.sa_family;
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ ife->metric = 0;
+ if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
+ ife->metric = ifr.ifr_metric;
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ ife->mtu = 0;
+ if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
+ ife->mtu = ifr.ifr_mtu;
+
+ memset(&ife->map, 0, sizeof(struct ifmap));
+#ifdef SIOCGIFMAP
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
+ ife->map = ifr.ifr_map;
+#endif
+
+#ifdef HAVE_TXQUEUELEN
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ ife->tx_queue_len = -1; /* unknown value */
+ if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
+ ife->tx_queue_len = ifr.ifr_qlen;
+#else
+ ife->tx_queue_len = -1; /* unknown value */
+#endif
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ ifr.ifr_addr.sa_family = AF_INET;
+ memset(&ife->addr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
+ ife->has_ip = 1;
+ ife->addr = ifr.ifr_addr;
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
+ ife->dstaddr = ifr.ifr_dstaddr;
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
+ ife->broadaddr = ifr.ifr_broadaddr;
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+ memset(&ife->netmask, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
+ ife->netmask = ifr.ifr_netmask;
+ }
+
+ close(skfd);
+ return 0;
+}
+
+static int do_if_fetch(struct interface *ife)
+{
+ if (if_fetch(ife) < 0) {
+ const char *errmsg;
+
+ if (errno == ENODEV) {
+ /* Give better error message for this case. */
+ errmsg = "Device not found";
+ } else {
+ errmsg = strerror(errno);
+ }
+ bb_error_msg("%s: error fetching interface information: %s",
+ ife->name, errmsg);
+ return -1;
+ }
+ return 0;
+}
+
+static const struct hwtype unspec_hwtype = {
+ .name = "unspec",
+ .title = "UNSPEC",
+ .type = -1,
+ .print = UNSPEC_print
+};
+
+static const struct hwtype loop_hwtype = {
+ .name = "loop",
+ .title = "Local Loopback",
+ .type = ARPHRD_LOOPBACK
+};
+
+#include <net/if_arp.h>
+
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+/* Display an Ethernet address in readable format. */
+static char* FAST_FUNC ether_print(unsigned char *ptr)
+{
+ static char *buff;
+
+ free(buff);
+ buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X",
+ (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
+ (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
+ );
+ return buff;
+}
+
+static int FAST_FUNC ether_input(const char *bufp, struct sockaddr *sap);
+
+static const struct hwtype ether_hwtype = {
+ .name = "ether",
+ .title = "Ethernet",
+ .type = ARPHRD_ETHER,
+ .alen = ETH_ALEN,
+ .print = ether_print,
+ .input = ether_input
+};
+
+static unsigned hexchar2int(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ c &= ~0x20; /* a -> A */
+ if ((unsigned)(c - 'A') <= 5)
+ return c - ('A' - 10);
+ return ~0U;
+}
+
+/* Input an Ethernet address and convert to binary. */
+static int FAST_FUNC ether_input(const char *bufp, struct sockaddr *sap)
+{
+ unsigned char *ptr;
+ char c;
+ int i;
+ unsigned val;
+
+ sap->sa_family = ether_hwtype.type;
+ ptr = (unsigned char*) sap->sa_data;
+
+ i = 0;
+ while ((*bufp != '\0') && (i < ETH_ALEN)) {
+ val = hexchar2int(*bufp++) * 0x10;
+ if (val > 0xff) {
+ errno = EINVAL;
+ return -1;
+ }
+ c = *bufp;
+ if (c == ':' || c == 0)
+ val >>= 4;
+ else {
+ val |= hexchar2int(c);
+ if (val > 0xff) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ if (c != 0)
+ bufp++;
+ *ptr++ = (unsigned char) val;
+ i++;
+
+ /* We might get a semicolon here - not required. */
+ if (*bufp == ':') {
+ bufp++;
+ }
+ }
+ return 0;
+}
+
+#include <net/if_arp.h>
+
+static const struct hwtype ppp_hwtype = {
+ .name = "ppp",
+ .title = "Point-to-Point Protocol",
+ .type = ARPHRD_PPP
+};
+
+#if ENABLE_FEATURE_IPV6
+static const struct hwtype sit_hwtype = {
+ .name = "sit",
+ .title = "IPv6-in-IPv4",
+ .type = ARPHRD_SIT,
+ .print = UNSPEC_print,
+ .suppress_null_addr = 1
+};
+#endif
+#if ENABLE_FEATURE_HWIB
+static const struct hwtype ib_hwtype = {
+ .name = "infiniband",
+ .title = "InfiniBand",
+ .type = ARPHRD_INFINIBAND,
+ .alen = INFINIBAND_ALEN,
+ .print = UNSPEC_print,
+ .input = in_ib,
+};
+#endif
+
+
+static const struct hwtype *const hwtypes[] = {
+ &loop_hwtype,
+ &ether_hwtype,
+ &ppp_hwtype,
+ &unspec_hwtype,
+#if ENABLE_FEATURE_IPV6
+ &sit_hwtype,
+#endif
+#if ENABLE_FEATURE_HWIB
+ &ib_hwtype,
+#endif
+ NULL
+};
+
+#ifdef IFF_PORTSEL
+static const char *const if_port_text[] = {
+ /* Keep in step with <linux/netdevice.h> */
+ "unknown",
+ "10base2",
+ "10baseT",
+ "AUI",
+ "100baseT",
+ "100baseTX",
+ "100baseFX",
+ NULL
+};
+#endif
+
+/* Check our hardware type table for this type. */
+const struct hwtype* FAST_FUNC get_hwtype(const char *name)
+{
+ const struct hwtype *const *hwp;
+
+ hwp = hwtypes;
+ while (*hwp != NULL) {
+ if (!strcmp((*hwp)->name, name))
+ return (*hwp);
+ hwp++;
+ }
+ return NULL;
+}
+
+/* Check our hardware type table for this type. */
+const struct hwtype* FAST_FUNC get_hwntype(int type)
+{
+ const struct hwtype *const *hwp;
+
+ hwp = hwtypes;
+ while (*hwp != NULL) {
+ if ((*hwp)->type == type)
+ return *hwp;
+ hwp++;
+ }
+ return NULL;
+}
+
+/* return 1 if address is all zeros */
+static int hw_null_address(const struct hwtype *hw, void *ap)
+{
+ int i;
+ unsigned char *address = (unsigned char *) ap;
+
+ for (i = 0; i < hw->alen; i++)
+ if (address[i])
+ return 0;
+ return 1;
+}
+
+static const char TRext[] ALIGN1 = "\0\0\0Ki\0Mi\0Gi\0Ti";
+
+static void print_bytes_scaled(unsigned long long ull, const char *end)
+{
+ unsigned long long int_part;
+ const char *ext;
+ unsigned int frac_part;
+ int i;
+
+ frac_part = 0;
+ ext = TRext;
+ int_part = ull;
+ i = 4;
+ do {
+ if (int_part >= 1024) {
+ frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
+ int_part /= 1024;
+ ext += 3; /* KiB, MiB, GiB, TiB */
+ }
+ --i;
+ } while (i);
+
+ printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
+}
+
+
+#ifdef HAVE_AFINET6
+#define IPV6_ADDR_ANY 0x0000U
+
+#define IPV6_ADDR_UNICAST 0x0001U
+#define IPV6_ADDR_MULTICAST 0x0002U
+#define IPV6_ADDR_ANYCAST 0x0004U
+
+#define IPV6_ADDR_LOOPBACK 0x0010U
+#define IPV6_ADDR_LINKLOCAL 0x0020U
+#define IPV6_ADDR_SITELOCAL 0x0040U
+
+#define IPV6_ADDR_COMPATv4 0x0080U
+
+#define IPV6_ADDR_SCOPE_MASK 0x00f0U
+
+#define IPV6_ADDR_MAPPED 0x1000U
+#define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */
+
+
+static void ife_print6(struct interface *ptr)
+{
+
+ FILE *f;
+ char addr6[40], devname[20];
+ struct sockaddr_in6 sap;
+ int plen, scope, dad_status, if_idx;
+ char addr6p[8][5];
+
+ f = fopen_for_read(_PATH_PROCNET_IFINET6);
+ if (f == NULL)
+ return;
+
+ while (fscanf
+ (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
+ addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
+ &dad_status, devname) != EOF
+ ) {
+ if (!strcmp(devname, ptr->name)) {
+ sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+ inet_pton(AF_INET6, addr6,
+ (struct sockaddr *) &sap.sin6_addr);
+ sap.sin6_family = AF_INET6;
+ printf(" inet6 addr: %s/%d",
+ INET6_sprint((struct sockaddr *) &sap, 1),
+ plen);
+ printf(" Scope:");
+ switch (scope & IPV6_ADDR_SCOPE_MASK) {
+ case 0:
+ puts("Global");
+ break;
+ case IPV6_ADDR_LINKLOCAL:
+ puts("Link");
+ break;
+ case IPV6_ADDR_SITELOCAL:
+ puts("Site");
+ break;
+ case IPV6_ADDR_COMPATv4:
+ puts("Compat");
+ break;
+ case IPV6_ADDR_LOOPBACK:
+ puts("Host");
+ break;
+ default:
+ puts("Unknown");
+ }
+ }
+ }
+ fclose(f);
+}
+#else
+#define ife_print6(a) ((void)0)
+#endif
+
+
+static void ife_print(struct interface *ptr)
+{
+ const struct aftype *ap;
+ const struct hwtype *hw;
+ int hf;
+ int can_compress = 0;
+
+ ap = get_afntype(ptr->addr.sa_family);
+ if (ap == NULL)
+ ap = get_afntype(0);
+
+ hf = ptr->type;
+
+ if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
+ can_compress = 1;
+
+ hw = get_hwntype(hf);
+ if (hw == NULL)
+ hw = get_hwntype(-1);
+
+ printf("%-10.10s Link encap:%s ", ptr->name, hw->title);
+ /* For some hardware types (eg Ash, ATM) we don't print the
+ hardware address if it's null. */
+ if (hw->print != NULL
+ && !(hw_null_address(hw, ptr->hwaddr) && hw->suppress_null_addr)
+ ) {
+ printf("HWaddr %s ", hw->print((unsigned char *)ptr->hwaddr));
+ }
+#ifdef IFF_PORTSEL
+ if (ptr->flags & IFF_PORTSEL) {
+ printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
+ if (ptr->flags & IFF_AUTOMEDIA)
+ printf("(auto)");
+ }
+#endif
+ bb_putchar('\n');
+
+ if (ptr->has_ip) {
+ printf(" %s addr:%s ", ap->name,
+ ap->sprint(&ptr->addr, 1));
+ if (ptr->flags & IFF_POINTOPOINT) {
+ printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
+ }
+ if (ptr->flags & IFF_BROADCAST) {
+ printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
+ }
+ printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
+ }
+
+ ife_print6(ptr);
+
+ printf(" ");
+ /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
+
+ if (ptr->flags == 0) {
+ printf("[NO FLAGS] ");
+ } else {
+ static const char ife_print_flags_strs[] ALIGN1 =
+ "UP\0"
+ "BROADCAST\0"
+ "DEBUG\0"
+ "LOOPBACK\0"
+ "POINTOPOINT\0"
+ "NOTRAILERS\0"
+ "RUNNING\0"
+ "NOARP\0"
+ "PROMISC\0"
+ "ALLMULTI\0"
+ "SLAVE\0"
+ "MASTER\0"
+ "MULTICAST\0"
+#ifdef HAVE_DYNAMIC
+ "DYNAMIC\0"
+#endif
+ ;
+ static const unsigned short ife_print_flags_mask[] ALIGN2 = {
+ IFF_UP,
+ IFF_BROADCAST,
+ IFF_DEBUG,
+ IFF_LOOPBACK,
+ IFF_POINTOPOINT,
+ IFF_NOTRAILERS,
+ IFF_RUNNING,
+ IFF_NOARP,
+ IFF_PROMISC,
+ IFF_ALLMULTI,
+ IFF_SLAVE,
+ IFF_MASTER,
+ IFF_MULTICAST
+#ifdef HAVE_DYNAMIC
+ ,IFF_DYNAMIC
+#endif
+ };
+ const unsigned short *mask = ife_print_flags_mask;
+ const char *str = ife_print_flags_strs;
+ do {
+ if (ptr->flags & *mask) {
+ printf("%s ", str);
+ }
+ mask++;
+ str += strlen(str) + 1;
+ } while (*str);
+ }
+
+ /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
+ printf(" MTU:%d Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
+#ifdef SIOCSKEEPALIVE
+ if (ptr->outfill || ptr->keepalive)
+ printf(" Outfill:%d Keepalive:%d", ptr->outfill, ptr->keepalive);
+#endif
+ bb_putchar('\n');
+
+ /* If needed, display the interface statistics. */
+
+ if (ptr->statistics_valid) {
+ /* XXX: statistics are currently only printed for the primary address,
+ * not for the aliases, although strictly speaking they're shared
+ * by all addresses.
+ */
+ printf(" ");
+
+ printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
+ ptr->stats.rx_packets, ptr->stats.rx_errors,
+ ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
+ ptr->stats.rx_frame_errors);
+ if (can_compress)
+ printf(" compressed:%lu\n",
+ ptr->stats.rx_compressed);
+ printf(" ");
+ printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
+ ptr->stats.tx_packets, ptr->stats.tx_errors,
+ ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
+ ptr->stats.tx_carrier_errors);
+ printf(" collisions:%lu ", ptr->stats.collisions);
+ if (can_compress)
+ printf("compressed:%lu ", ptr->stats.tx_compressed);
+ if (ptr->tx_queue_len != -1)
+ printf("txqueuelen:%d ", ptr->tx_queue_len);
+ printf("\n R");
+ print_bytes_scaled(ptr->stats.rx_bytes, " T");
+ print_bytes_scaled(ptr->stats.tx_bytes, "\n");
+ }
+
+ if (ptr->map.irq || ptr->map.mem_start
+ || ptr->map.dma || ptr->map.base_addr
+ ) {
+ printf(" ");
+ if (ptr->map.irq)
+ printf("Interrupt:%d ", ptr->map.irq);
+ if (ptr->map.base_addr >= 0x100) /* Only print devices using it for
+ I/O maps */
+ printf("Base address:0x%lx ",
+ (unsigned long) ptr->map.base_addr);
+ if (ptr->map.mem_start) {
+ printf("Memory:%lx-%lx ", ptr->map.mem_start,
+ ptr->map.mem_end);
+ }
+ if (ptr->map.dma)
+ printf("DMA chan:%x ", ptr->map.dma);
+ bb_putchar('\n');
+ }
+ bb_putchar('\n');
+}
+
+static int do_if_print(struct interface *ife) /*, int *opt_a)*/
+{
+ int res;
+
+ res = do_if_fetch(ife);
+ if (res >= 0) {
+ if ((ife->flags & IFF_UP) || interface_opt_a)
+ ife_print(ife);
+ }
+ return res;
+}
+
+static struct interface *lookup_interface(char *name)
+{
+ struct interface *ife = NULL;
+
+ if (if_readlist_proc(name) < 0)
+ return NULL;
+ ife = add_interface(name);
+ return ife;
+}
+
+#ifdef UNUSED
+static int for_all_interfaces(int (*doit) (struct interface *, void *),
+ void *cookie)
+{
+ struct interface *ife;
+
+ if (!int_list && (if_readlist() < 0))
+ return -1;
+ for (ife = int_list; ife; ife = ife->next) {
+ int err = doit(ife, cookie);
+
+ if (err)
+ return err;
+ }
+ return 0;
+}
+#endif
+
+/* for ipv4 add/del modes */
+static int if_print(char *ifname)
+{
+ struct interface *ife;
+ int res;
+
+ if (!ifname) {
+ /*res = for_all_interfaces(do_if_print, &interface_opt_a);*/
+ if (!int_list && (if_readlist() < 0))
+ return -1;
+ for (ife = int_list; ife; ife = ife->next) {
+ int err = do_if_print(ife); /*, &interface_opt_a);*/
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+ ife = lookup_interface(ifname);
+ res = do_if_fetch(ife);
+ if (res >= 0)
+ ife_print(ife);
+ return res;
+}
+
+#if ENABLE_FEATURE_HWIB
+/* Input an Infiniband address and convert to binary. */
+int FAST_FUNC in_ib(const char *bufp, struct sockaddr *sap)
+{
+ unsigned char *ptr;
+ char c;
+ const char *orig;
+ int i;
+ unsigned val;
+
+ sap->sa_family = ib_hwtype.type;
+ ptr = (unsigned char *) sap->sa_data;
+
+ i = 0;
+ orig = bufp;
+ while ((*bufp != '\0') && (i < INFINIBAND_ALEN)) {
+ val = 0;
+ c = *bufp++;
+ if (isdigit(c))
+ val = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ val = c - 'A' + 10;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+ val <<= 4;
+ c = *bufp;
+ if (isdigit(c))
+ val |= c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val |= c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ val |= c - 'A' + 10;
+ else if (c == ':' || c == 0)
+ val >>= 4;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+ if (c != 0)
+ bufp++;
+ *ptr++ = (unsigned char) (val & 0377);
+ i++;
+
+ /* We might get a semicolon here - not required. */
+ if (*bufp == ':') {
+ bufp++;
+ }
+ }
+#ifdef DEBUG
+ fprintf(stderr, "in_ib(%s): %s\n", orig, UNSPEC_print(sap->sa_data));
+#endif
+ return 0;
+}
+#endif
+
+
+int FAST_FUNC display_interfaces(char *ifname)
+{
+ int status;
+
+ status = if_print(ifname);
+
+ return (status < 0); /* status < 0 == 1 -- error */
+}
diff --git a/release/src/router/busybox/networking/ip.c b/release/src/router/busybox/networking/ip.c
index a0781bdb..9903c680 100644
--- a/release/src/router/busybox/networking/ip.c
+++ b/release/src/router/busybox/networking/ip.c
@@ -1,10 +1,8 @@
+/* vi: set sw=4 ts=4: */
/*
* ip.c "ip" utility frontend.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
@@ -12,104 +10,114 @@
* Changes:
*
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Bernhard Reutner-Fischer rewrote to use index_in_substr_array
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <string.h>
+#include "libbb.h"
-#include "./libiproute/utils.h"
-#include "./libiproute/ip_common.h"
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
-#include "busybox.h"
+#if ENABLE_FEATURE_IP_ADDRESS \
+ || ENABLE_FEATURE_IP_ROUTE \
+ || ENABLE_FEATURE_IP_LINK \
+ || ENABLE_FEATURE_IP_TUNNEL \
+ || ENABLE_FEATURE_IP_RULE
-#if 0
-int preferred_family = AF_UNSPEC;
-int oneline = 0;
-char * _SL_ = NULL;
-
-void ip_parse_common_args(int *argcp, char ***argvp)
+static int NORETURN ip_print_help(char **argv UNUSED_PARAM)
{
- int argc = *argcp;
- char **argv = *argvp;
-
- while (argc > 1) {
- char *opt = argv[1];
-
- if (strcmp(opt,"--") == 0) {
- argc--; argv++;
- break;
- }
-
- if (opt[0] != '-')
- break;
+ bb_show_usage();
+}
- if (opt[1] == '-')
- opt++;
+static int ip_do(int (*ip_func)(char **argv), char **argv)
+{
+ argv = ip_parse_common_args(argv + 1);
+ return ip_func(argv);
+}
- if (matches(opt, "-family") == 0) {
- argc--;
- argv++;
- if (strcmp(argv[1], "inet") == 0)
- preferred_family = AF_INET;
- else if (strcmp(argv[1], "inet6") == 0)
- preferred_family = AF_INET6;
- else if (strcmp(argv[1], "link") == 0)
- preferred_family = AF_PACKET;
- else
- invarg(argv[1], "invalid protocol family");
- } else if (strcmp(opt, "-4") == 0) {
- preferred_family = AF_INET;
- } else if (strcmp(opt, "-6") == 0) {
- preferred_family = AF_INET6;
- } else if (strcmp(opt, "-0") == 0) {
- preferred_family = AF_PACKET;
- } else if (matches(opt, "-oneline") == 0) {
- ++oneline;
- } else {
- bb_show_usage();
- }
- argc--; argv++;
- }
- _SL_ = oneline ? "\\" : "\n" ;
+#if ENABLE_FEATURE_IP_ADDRESS
+int ipaddr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipaddr_main(int argc UNUSED_PARAM, char **argv)
+{
+ return ip_do(do_ipaddr, argv);
}
#endif
-
-int ip_main(int argc, char **argv)
+#if ENABLE_FEATURE_IP_LINK
+int iplink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iplink_main(int argc UNUSED_PARAM, char **argv)
+{
+ return ip_do(do_iplink, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_ROUTE
+int iproute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iproute_main(int argc UNUSED_PARAM, char **argv)
{
- int ret = EXIT_FAILURE;
+ return ip_do(do_iproute, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_RULE
+int iprule_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iprule_main(int argc UNUSED_PARAM, char **argv)
+{
+ return ip_do(do_iprule, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_TUNNEL
+int iptunnel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iptunnel_main(int argc UNUSED_PARAM, char **argv)
+{
+ return ip_do(do_iptunnel, argv);
+}
+#endif
+
- ip_parse_common_args(&argc, &argv);
+int ip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ip_main(int argc UNUSED_PARAM, char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ USE_FEATURE_IP_ADDRESS("address\0")
+ USE_FEATURE_IP_ROUTE("route\0")
+ USE_FEATURE_IP_LINK("link\0")
+ USE_FEATURE_IP_TUNNEL("tunnel\0" "tunl\0")
+ USE_FEATURE_IP_RULE("rule\0")
+ ;
+ enum {
+ USE_FEATURE_IP_ADDRESS(IP_addr,)
+ USE_FEATURE_IP_ROUTE(IP_route,)
+ USE_FEATURE_IP_LINK(IP_link,)
+ USE_FEATURE_IP_TUNNEL(IP_tunnel, IP_tunl,)
+ USE_FEATURE_IP_RULE(IP_rule,)
+ IP_none
+ };
+ int (*ip_func)(char**) = ip_print_help;
- if (argc > 1) {
-#ifdef CONFIG_FEATURE_IP_ADDRESS
- if (matches(argv[1], "address") == 0) {
- ret = do_ipaddr(argc-2, argv+2);
- }
+ argv = ip_parse_common_args(argv + 1);
+ if (*argv) {
+ int key = index_in_substrings(keywords, *argv);
+ argv++;
+#if ENABLE_FEATURE_IP_ADDRESS
+ if (key == IP_addr)
+ ip_func = do_ipaddr;
#endif
-#ifdef CONFIG_FEATURE_IP_ROUTE
- else if (matches(argv[1], "route") == 0) {
- ret = do_iproute(argc-2, argv+2);
- }
+#if ENABLE_FEATURE_IP_ROUTE
+ if (key == IP_route)
+ ip_func = do_iproute;
#endif
-#ifdef CONFIG_FEATURE_IP_LINK
- else if (matches(argv[1], "link") == 0) {
- ret = do_iplink(argc-2, argv+2);
- }
+#if ENABLE_FEATURE_IP_LINK
+ if (key == IP_link)
+ ip_func = do_iplink;
#endif
-#ifdef CONFIG_FEATURE_IP_TUNNEL
- else if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) {
- ret = do_iptunnel(argc-2, argv+2);
- }
+#if ENABLE_FEATURE_IP_TUNNEL
+ if (key == IP_tunnel || key == IP_tunl)
+ ip_func = do_iptunnel;
+#endif
+#if ENABLE_FEATURE_IP_RULE
+ if (key == IP_rule)
+ ip_func = do_iprule;
#endif
}
- if (ret) {
- bb_show_usage();
- }
- return(EXIT_SUCCESS);
+ return ip_func(argv);
}
+
+#endif /* any of ENABLE_FEATURE_IP_xxx is 1 */
diff --git a/release/src/router/busybox/networking/ipaddr.c b/release/src/router/busybox/networking/ipaddr.c
deleted file mode 100644
index 7826194c..00000000
--- a/release/src/router/busybox/networking/ipaddr.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * ip.c "ip" utility frontend.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- *
- *
- * Changes:
- *
- * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
- */
-
-#include "./libiproute/utils.h"
-#include "./libiproute/ip_common.h"
-
-#include "busybox.h"
-
-int ipaddr_main(int argc, char **argv)
-{
- ip_parse_common_args(&argc, &argv);
-
- return do_ipaddr(argc-1, argv+1);
-}
diff --git a/release/src/router/busybox/networking/ipcalc.c b/release/src/router/busybox/networking/ipcalc.c
index 94b747ef..d8fa5f3f 100644
--- a/release/src/router/busybox/networking/ipcalc.c
+++ b/release/src/router/busybox/networking/ipcalc.c
@@ -1,26 +1,21 @@
-/* vi: set sw=4 ts=4 ai: */
+/* vi: set sw=4 ts=4: */
/*
* Mini ipcalc implementation for busybox
*
* By Jordan Crouse <jordan@cosmicpenguin.net>
* Stephan Linz <linz@li-pro.net>
*
- * This is a complete reimplentation of the ipcalc program
- * from Redhat. I didn't look at their source code, but there
+ * This is a complete reimplementation of the ipcalc program
+ * from Red Hat. I didn't look at their source code, but there
* is no denying that this is a loving reimplementation
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <getopt.h>
#include <sys/socket.h>
-#include <netinet/in.h>
#include <arpa/inet.h>
-#include "busybox.h"
-
-#define IPCALC_MSG(CMD,ALTCMD) if (mode & SILENT) {ALTCMD;} else {CMD;}
+#include "libbb.h"
#define CLASS_A_NETMASK ntohl(0xFF000000)
#define CLASS_B_NETMASK ntohl(0xFFFF0000)
@@ -40,111 +35,156 @@ static unsigned long get_netmask(unsigned long ipaddr)
return 0;
}
+#if ENABLE_FEATURE_IPCALC_FANCY
+static int get_prefix(unsigned long netmask)
+{
+ unsigned long msk = 0x80000000;
+ int ret = 0;
+
+ netmask = htonl(netmask);
+ while (msk) {
+ if (netmask & msk)
+ ret++;
+ msk >>= 1;
+ }
+ return ret;
+}
+#else
+int get_prefix(unsigned long netmask);
+#endif
+
+
#define NETMASK 0x01
#define BROADCAST 0x02
#define NETWORK 0x04
-#define HOSTNAME 0x08
-#define SILENT 0x10
+#define NETPREFIX 0x08
+#define HOSTNAME 0x10
+#define SILENT 0x20
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+ static const char ipcalc_longopts[] ALIGN1 =
+ "netmask\0" No_argument "m"
+ "broadcast\0" No_argument "b"
+ "network\0" No_argument "n"
+# if ENABLE_FEATURE_IPCALC_FANCY
+ "prefix\0" No_argument "p"
+ "hostname\0" No_argument "h"
+ "silent\0" No_argument "s"
+# endif
+ ;
+#endif
+int ipcalc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ipcalc_main(int argc, char **argv)
{
- unsigned long mode;
-
- unsigned long netmask = 0;
- unsigned long broadcast;
- unsigned long network;
- unsigned long ipaddr;
-
- static const struct option long_options[] = {
- {"netmask", no_argument, NULL, 'n'},
- {"broadcast", no_argument, NULL, 'b'},
- {"network", no_argument, NULL, 'w'},
-#ifdef CONFIG_FEATURE_IPCALC_FANCY
- {"hostname", no_argument, NULL, 'h'},
- {"silent", no_argument, NULL, 's'},
+ unsigned opt;
+ int have_netmask = 0;
+ in_addr_t netmask, broadcast, network, ipaddr;
+ struct in_addr a;
+ char *ipstr;
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+ applet_long_options = ipcalc_longopts;
#endif
- {NULL, 0, NULL, 0}
- };
-
- bb_applet_long_options = long_options;
- mode = bb_getopt_ulflags(argc, argv,
-#ifdef CONFIG_FEATURE_IPCALC_FANCY
- "nbwhs");
-#else
- "nbw");
-#endif
- if (mode & (BROADCAST | NETWORK)) {
- if (argc - optind > 2) {
+ opt = getopt32(argv, "mbn" USE_FEATURE_IPCALC_FANCY("phs"));
+ argc -= optind;
+ argv += optind;
+ if (opt & (BROADCAST | NETWORK | NETPREFIX)) {
+ if (argc > 2 || argc <= 0)
bb_show_usage();
- }
} else {
- if (argc - optind != 1) {
+ if (argc != 1)
bb_show_usage();
+ }
+ if (opt & SILENT)
+ logmode = LOGMODE_NONE; /* Suppress error_msg() output */
+
+ ipstr = argv[0];
+ if (ENABLE_FEATURE_IPCALC_FANCY) {
+ unsigned long netprefix = 0;
+ char *prefixstr;
+
+ prefixstr = ipstr;
+
+ while (*prefixstr) {
+ if (*prefixstr == '/') {
+ *prefixstr = (char)0;
+ prefixstr++;
+ if (*prefixstr) {
+ unsigned msk;
+ netprefix = xatoul_range(prefixstr, 0, 32);
+ netmask = 0;
+ msk = 0x80000000;
+ while (netprefix > 0) {
+ netmask |= msk;
+ msk >>= 1;
+ netprefix--;
+ }
+ netmask = htonl(netmask);
+ /* Even if it was 0, we will signify that we have a netmask. This allows */
+ /* for specification of default routes, etc which have a 0 netmask/prefix */
+ have_netmask = 1;
+ }
+ break;
+ }
+ prefixstr++;
}
}
+ ipaddr = inet_aton(ipstr, &a);
- ipaddr = inet_addr(argv[optind]);
-
- if (ipaddr == INADDR_NONE) {
- IPCALC_MSG(bb_error_msg_and_die("bad IP address: %s", argv[optind]),
- exit(EXIT_FAILURE));
+ if (ipaddr == 0) {
+ bb_error_msg_and_die("bad IP address: %s", argv[0]);
}
+ ipaddr = a.s_addr;
+ if (argc == 2) {
+ if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) {
+ bb_error_msg_and_die("use prefix or netmask, not both");
+ }
- if (argc - optind == 2) {
- netmask = inet_addr(argv[optind + 1]);
- }
-
- if (ipaddr == INADDR_NONE) {
- IPCALC_MSG(bb_error_msg_and_die("bad netmask: %s", argv[optind + 1]),
- exit(EXIT_FAILURE));
- }
+ netmask = inet_aton(argv[1], &a);
+ if (netmask == 0) {
+ bb_error_msg_and_die("bad netmask: %s", argv[1]);
+ }
+ netmask = a.s_addr;
+ } else {
- /* JHC - If the netmask wasn't provided then calculate it */
- if (!netmask) {
- netmask = get_netmask(ipaddr);
+ /* JHC - If the netmask wasn't provided then calculate it */
+ if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask)
+ netmask = get_netmask(ipaddr);
}
- if (mode & NETMASK) {
+ if (opt & NETMASK) {
printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask)));
}
- if (mode & BROADCAST) {
+ if (opt & BROADCAST) {
broadcast = (ipaddr & netmask) | ~netmask;
printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast)));
}
- if (mode & NETWORK) {
+ if (opt & NETWORK) {
network = ipaddr & netmask;
printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network)));
}
-#ifdef CONFIG_FEATURE_IPCALC_FANCY
- if (mode & HOSTNAME) {
- struct hostent *hostinfo;
- int x;
-
- hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
- if (!hostinfo) {
- IPCALC_MSG(bb_herror_msg_and_die(
- "cannot find hostname for %s", argv[optind]),);
- exit(EXIT_FAILURE);
- }
- for (x = 0; hostinfo->h_name[x]; x++) {
- hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
+
+ if (ENABLE_FEATURE_IPCALC_FANCY) {
+ if (opt & NETPREFIX) {
+ printf("PREFIX=%i\n", get_prefix(netmask));
}
- printf("HOSTNAME=%s\n", hostinfo->h_name);
+ if (opt & HOSTNAME) {
+ struct hostent *hostinfo;
+
+ hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
+ if (!hostinfo) {
+ bb_herror_msg_and_die("cannot find hostname for %s", argv[0]);
+ }
+ str_tolower(hostinfo->h_name);
+
+ printf("HOSTNAME=%s\n", hostinfo->h_name);
+ }
}
-#endif
return EXIT_SUCCESS;
}
-
-/* END CODE */
-/*
-Local Variables:
-c-file-style: "linux"
-c-basic-offset: 4
-tab-width: 4
-End:
-*/
diff --git a/release/src/router/busybox/networking/iplink.c b/release/src/router/busybox/networking/iplink.c
deleted file mode 100644
index 7207e176..00000000
--- a/release/src/router/busybox/networking/iplink.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * ip.c "ip" utility frontend.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- *
- *
- * Changes:
- *
- * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
- */
-
-#include "./libiproute/utils.h"
-#include "./libiproute/ip_common.h"
-
-#include "busybox.h"
-
-int iplink_main(int argc, char **argv)
-{
- ip_parse_common_args(&argc, &argv);
-
- return do_iplink(argc-1, argv+1);
-}
diff --git a/release/src/router/busybox/networking/iproute.c b/release/src/router/busybox/networking/iproute.c
deleted file mode 100644
index d049a87c..00000000
--- a/release/src/router/busybox/networking/iproute.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * ip.c "ip" utility frontend.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- *
- *
- * Changes:
- *
- * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
- */
-
-#include "./libiproute/utils.h"
-#include "./libiproute/ip_common.h"
-
-#include "busybox.h"
-
-int iproute_main(int argc, char **argv)
-{
- ip_parse_common_args(&argc, &argv);
-
- return do_iproute(argc-1, argv+1);
-}
diff --git a/release/src/router/busybox/networking/iptunnel.c b/release/src/router/busybox/networking/iptunnel.c
deleted file mode 100644
index f2b2e8a7..00000000
--- a/release/src/router/busybox/networking/iptunnel.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * ip.c "ip" utility frontend.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- *
- *
- * Changes:
- *
- * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
- */
-
-#include "./libiproute/utils.h"
-#include "./libiproute/ip_common.h"
-
-#include "busybox.h"
-
-int iptunnel_main(int argc, char **argv)
-{
- ip_parse_common_args(&argc, &argv);
-
- return do_iptunnel(argc-1, argv+1);
-}
diff --git a/release/src/router/busybox/networking/isrv.c b/release/src/router/busybox/networking/isrv.c
new file mode 100644
index 00000000..66bb3718
--- /dev/null
+++ b/release/src/router/busybox/networking/isrv.c
@@ -0,0 +1,338 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+/* Opaque structure */
+
+struct isrv_state_t {
+ short *fd2peer; /* one per registered fd */
+ void **param_tbl; /* one per registered peer */
+ /* one per registered peer; doesn't exist if !timeout */
+ time_t *timeo_tbl;
+ int (*new_peer)(isrv_state_t *state, int fd);
+ time_t curtime;
+ int timeout;
+ int fd_count;
+ int peer_count;
+ int wr_count;
+ fd_set rd;
+ fd_set wr;
+};
+#define FD2PEER (state->fd2peer)
+#define PARAM_TBL (state->param_tbl)
+#define TIMEO_TBL (state->timeo_tbl)
+#define CURTIME (state->curtime)
+#define TIMEOUT (state->timeout)
+#define FD_COUNT (state->fd_count)
+#define PEER_COUNT (state->peer_count)
+#define WR_COUNT (state->wr_count)
+
+/* callback */
+void isrv_want_rd(isrv_state_t *state, int fd)
+{
+ FD_SET(fd, &state->rd);
+}
+
+/* callback */
+void isrv_want_wr(isrv_state_t *state, int fd)
+{
+ if (!FD_ISSET(fd, &state->wr)) {
+ WR_COUNT++;
+ FD_SET(fd, &state->wr);
+ }
+}
+
+/* callback */
+void isrv_dont_want_rd(isrv_state_t *state, int fd)
+{
+ FD_CLR(fd, &state->rd);
+}
+
+/* callback */
+void isrv_dont_want_wr(isrv_state_t *state, int fd)
+{
+ if (FD_ISSET(fd, &state->wr)) {
+ WR_COUNT--;
+ FD_CLR(fd, &state->wr);
+ }
+}
+
+/* callback */
+int isrv_register_fd(isrv_state_t *state, int peer, int fd)
+{
+ int n;
+
+ DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd);
+
+ if (FD_COUNT >= FD_SETSIZE) return -1;
+ if (FD_COUNT <= fd) {
+ n = FD_COUNT;
+ FD_COUNT = fd + 1;
+
+ DPRINTF("register_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ while (n < fd) FD2PEER[n++] = -1;
+ }
+
+ DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer);
+
+ FD2PEER[fd] = peer;
+ return 0;
+}
+
+/* callback */
+void isrv_close_fd(isrv_state_t *state, int fd)
+{
+ DPRINTF("close_fd(%d)", fd);
+
+ close(fd);
+ isrv_dont_want_rd(state, fd);
+ if (WR_COUNT) isrv_dont_want_wr(state, fd);
+
+ FD2PEER[fd] = -1;
+ if (fd == FD_COUNT-1) {
+ do fd--; while (fd >= 0 && FD2PEER[fd] == -1);
+ FD_COUNT = fd + 1;
+
+ DPRINTF("close_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ }
+}
+
+/* callback */
+int isrv_register_peer(isrv_state_t *state, void *param)
+{
+ int n;
+
+ if (PEER_COUNT >= FD_SETSIZE) return -1;
+ n = PEER_COUNT++;
+
+ DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT);
+
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ PARAM_TBL[n] = param;
+ if (TIMEOUT) {
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+ TIMEO_TBL[n] = CURTIME;
+ }
+ return n;
+}
+
+static void remove_peer(isrv_state_t *state, int peer)
+{
+ int movesize;
+ int fd;
+
+ DPRINTF("remove_peer(%d)", peer);
+
+ fd = FD_COUNT - 1;
+ while (fd >= 0) {
+ if (FD2PEER[fd] == peer) {
+ isrv_close_fd(state, fd);
+ fd--;
+ continue;
+ }
+ if (FD2PEER[fd] > peer)
+ FD2PEER[fd]--;
+ fd--;
+ }
+
+ PEER_COUNT--;
+ DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT);
+
+ movesize = (PEER_COUNT - peer) * sizeof(void*);
+ if (movesize > 0) {
+ memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize);
+ if (TIMEOUT)
+ memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize);
+ }
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ if (TIMEOUT)
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+}
+
+static void handle_accept(isrv_state_t *state, int fd)
+{
+ int n, newfd;
+
+ /* suppress gcc warning "cast from ptr to int of different size" */
+ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK);
+ newfd = accept(fd, NULL, 0);
+ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]));
+ if (newfd < 0) {
+ if (errno == EAGAIN) return;
+ /* Most probably someone gave us wrong fd type
+ * (for example, non-socket). Don't want
+ * to loop forever. */
+ bb_perror_msg_and_die("accept");
+ }
+
+ DPRINTF("new_peer(%d)", newfd);
+ n = state->new_peer(state, newfd);
+ if (n)
+ remove_peer(state, n); /* unsuccesful peer start */
+}
+
+void BUG_sizeof_fd_set_is_strange(void);
+static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
+{
+ enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
+ int fds_pos;
+ int fd, peer;
+ /* need to know value at _the beginning_ of this routine */
+ int fd_cnt = FD_COUNT;
+
+ if (LONG_CNT * sizeof(long) != sizeof(fd_set))
+ BUG_sizeof_fd_set_is_strange();
+
+ fds_pos = 0;
+ while (1) {
+ /* Find next nonzero bit */
+ while (fds_pos < LONG_CNT) {
+ if (((long*)fds)[fds_pos] == 0) {
+ fds_pos++;
+ continue;
+ }
+ /* Found non-zero word */
+ fd = fds_pos * sizeof(long)*8; /* word# -> bit# */
+ while (1) {
+ if (FD_ISSET(fd, fds)) {
+ FD_CLR(fd, fds);
+ goto found_fd;
+ }
+ fd++;
+ }
+ }
+ break; /* all words are zero */
+ found_fd:
+ if (fd >= fd_cnt) { /* paranoia */
+ DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)",
+ fd, fd_cnt);
+ break;
+ }
+ DPRINTF("handle_fd_set: fd %d is active", fd);
+ peer = FD2PEER[fd];
+ if (peer < 0)
+ continue; /* peer is already gone */
+ if (peer == 0) {
+ handle_accept(state, fd);
+ continue;
+ }
+ DPRINTF("h(fd:%d)", fd);
+ if (h(fd, &PARAM_TBL[peer])) {
+ /* this peer is gone */
+ remove_peer(state, peer);
+ } else if (TIMEOUT) {
+ TIMEO_TBL[peer] = monotonic_sec();
+ }
+ }
+}
+
+static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
+{
+ int n, peer;
+ peer = PEER_COUNT-1;
+ /* peer 0 is not checked */
+ while (peer > 0) {
+ DPRINTF("peer %d: time diff %d", peer,
+ (int)(CURTIME - TIMEO_TBL[peer]));
+ if ((CURTIME - TIMEO_TBL[peer]) >= TIMEOUT) {
+ DPRINTF("peer %d: do_timeout()", peer);
+ n = do_timeout(&PARAM_TBL[peer]);
+ if (n)
+ remove_peer(state, peer);
+ }
+ peer--;
+ }
+}
+
+/* Driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int linger_timeout)
+{
+ isrv_state_t *state = xzalloc(sizeof(*state));
+ state->new_peer = new_peer;
+ state->timeout = timeout;
+
+ /* register "peer" #0 - it will accept new connections */
+ isrv_register_peer(state, NULL);
+ isrv_register_fd(state, /*peer:*/ 0, listen_fd);
+ isrv_want_rd(state, listen_fd);
+ /* remember flags to make blocking<->nonblocking switch faster */
+ /* (suppress gcc warning "cast from ptr to int of different size") */
+ PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL));
+
+ while (1) {
+ struct timeval tv;
+ fd_set rd;
+ fd_set wr;
+ fd_set *wrp = NULL;
+ int n;
+
+ tv.tv_sec = timeout;
+ if (PEER_COUNT <= 1)
+ tv.tv_sec = linger_timeout;
+ tv.tv_usec = 0;
+ rd = state->rd;
+ if (WR_COUNT) {
+ wr = state->wr;
+ wrp = &wr;
+ }
+
+ DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...",
+ FD_COUNT, (int)tv.tv_sec);
+ n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL);
+ DPRINTF("run: ...select:%d", n);
+
+ if (n < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("select");
+ continue;
+ }
+
+ if (n == 0 && linger_timeout && PEER_COUNT <= 1)
+ break;
+
+ if (timeout) {
+ time_t t = monotonic_sec();
+ if (t != CURTIME) {
+ CURTIME = t;
+ handle_timeout(state, do_timeout);
+ }
+ }
+ if (n > 0) {
+ handle_fd_set(state, &rd, do_rd);
+ if (wrp)
+ handle_fd_set(state, wrp, do_wr);
+ }
+ }
+ DPRINTF("run: bailout");
+ /* NB: accept socket is not closed. Caller is to decide what to do */
+}
diff --git a/release/src/router/busybox/networking/isrv.h b/release/src/router/busybox/networking/isrv.h
new file mode 100644
index 00000000..f20714df
--- /dev/null
+++ b/release/src/router/busybox/networking/isrv.h
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* opaque structure */
+struct isrv_state_t;
+typedef struct isrv_state_t isrv_state_t;
+
+/* callbacks */
+void isrv_want_rd(isrv_state_t *state, int fd);
+void isrv_want_wr(isrv_state_t *state, int fd);
+void isrv_dont_want_rd(isrv_state_t *state, int fd);
+void isrv_dont_want_wr(isrv_state_t *state, int fd);
+int isrv_register_fd(isrv_state_t *state, int peer, int fd);
+void isrv_close_fd(isrv_state_t *state, int fd);
+int isrv_register_peer(isrv_state_t *state, void *param);
+
+/* driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int linger_timeout
+);
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/release/src/router/busybox/networking/isrv_identd.c b/release/src/router/busybox/networking/isrv_identd.c
new file mode 100644
index 00000000..e8ba0076
--- /dev/null
+++ b/release/src/router/busybox/networking/isrv_identd.c
@@ -0,0 +1,147 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Fake identd server.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include "isrv.h"
+
+enum { TIMEOUT = 20 };
+
+typedef struct identd_buf_t {
+ int pos;
+ int fd_flag;
+ char buf[64 - 2*sizeof(int)];
+} identd_buf_t;
+
+#define bogouser bb_common_bufsiz1
+
+static int new_peer(isrv_state_t *state, int fd)
+{
+ int peer;
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+
+ peer = isrv_register_peer(state, buf);
+ if (peer < 0)
+ return 0; /* failure */
+ if (isrv_register_fd(state, peer, fd) < 0)
+ return peer; /* failure, unregister peer */
+
+ buf->fd_flag = fcntl(fd, F_GETFL) | O_NONBLOCK;
+ isrv_want_rd(state, fd);
+ return 0;
+}
+
+static int do_rd(int fd, void **paramp)
+{
+ identd_buf_t *buf = *paramp;
+ char *cur, *p;
+ int retval = 0; /* session is ok (so far) */
+ int sz;
+
+ cur = buf->buf + buf->pos;
+
+ if (buf->fd_flag & O_NONBLOCK)
+ fcntl(fd, F_SETFL, buf->fd_flag);
+ sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+
+ if (sz < 0) {
+ if (errno != EAGAIN)
+ goto term; /* terminate this session if !EAGAIN */
+ goto ok;
+ }
+
+ buf->pos += sz;
+ buf->buf[buf->pos] = '\0';
+ p = strpbrk(cur, "\r\n");
+ if (p)
+ *p = '\0';
+ if (!p && sz && buf->pos <= (int)sizeof(buf->buf))
+ goto ok;
+ /* Terminate session. If we are in server mode, then
+ * fd is still in nonblocking mode - we never block here */
+ if (fd == 0) fd++; /* inetd mode? then write to fd 1 */
+ fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+ term:
+ free(buf);
+ retval = 1; /* terminate */
+ ok:
+ if (buf->fd_flag & O_NONBLOCK)
+ fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+ return retval;
+}
+
+static int do_timeout(void **paramp UNUSED_PARAM)
+{
+ return 1; /* terminate session */
+}
+
+static void inetd_mode(void)
+{
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+ /* buf->pos = 0; - xzalloc did it */
+ /* We do NOT want nonblocking I/O here! */
+ /* buf->fd_flag = 0; - xzalloc did it */
+ do
+ alarm(TIMEOUT);
+ while (do_rd(0, (void*)&buf) == 0);
+}
+
+int fakeidentd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fakeidentd_main(int argc UNUSED_PARAM, char **argv)
+{
+ enum {
+ OPT_foreground = 0x1,
+ OPT_inetd = 0x2,
+ OPT_inetdwait = 0x4,
+ OPT_fiw = 0x7,
+ OPT_bindaddr = 0x8,
+ };
+
+ const char *bind_address = NULL;
+ unsigned opt;
+ int fd;
+
+ opt = getopt32(argv, "fiwb:", &bind_address);
+ strcpy(bogouser, "nobody");
+ if (argv[optind])
+ strncpy(bogouser, argv[optind], sizeof(bogouser));
+
+ /* Daemonize if no -f and no -i and no -w */
+ if (!(opt & OPT_fiw))
+ bb_daemonize_or_rexec(0, argv);
+
+ /* Where to log in inetd modes? "Classic" inetd
+ * probably has its stderr /dev/null'ed (we need log to syslog?),
+ * but daemontools-like utilities usually expect that children
+ * log to stderr. I like daemontools more. Go their way.
+ * (Or maybe we need yet another option "log to syslog") */
+ if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) {
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ if (opt & OPT_inetd) {
+ inetd_mode();
+ return 0;
+ }
+
+ /* Ignore closed connections when writing */
+ signal(SIGPIPE, SIG_IGN);
+
+ fd = 0;
+ if (!(opt & OPT_inetdwait)) {
+ fd = create_and_bind_stream_or_die(bind_address,
+ bb_lookup_port("identd", "tcp", 113));
+ xlisten(fd, 5);
+ }
+
+ isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout,
+ TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0);
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/libiproute/Kbuild b/release/src/router/busybox/networking/libiproute/Kbuild
new file mode 100644
index 00000000..5f9dd32b
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/Kbuild
@@ -0,0 +1,64 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+
+lib-$(CONFIG_SLATTACH) += \
+ utils.o
+
+lib-$(CONFIG_IP) += \
+ ip_parse_common_args.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_proto.o \
+ ll_types.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_ADDRESS) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_LINK) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_ROUTE) += \
+ ip_parse_common_args.o \
+ iproute.o \
+ libnetlink.o \
+ ll_map.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_TUNNEL) += \
+ ip_parse_common_args.o \
+ iptunnel.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_RULE) += \
+ ip_parse_common_args.o \
+ iprule.o \
+ rt_names.o \
+ utils.o
diff --git a/release/src/router/busybox/networking/libiproute/Makefile b/release/src/router/busybox/networking/libiproute/Makefile
deleted file mode 100644
index c1f18be7..00000000
--- a/release/src/router/busybox/networking/libiproute/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-TOPDIR:= ../../
-LIBIPROUTE_DIR:=./
-include $(TOPDIR).config
-include $(TOPDIR)Rules.mak
-include Makefile.in
-all: $(libraries-y)
--include $(TOPDIR).depend
-
-clean:
- rm -f *.o *.a $(AR_TARGET)
-
diff --git a/release/src/router/busybox/networking/libiproute/Makefile.in b/release/src/router/busybox/networking/libiproute/Makefile.in
deleted file mode 100755
index f39eeb3e..00000000
--- a/release/src/router/busybox/networking/libiproute/Makefile.in
+++ /dev/null
@@ -1,44 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-LIBIPROUTE_AR:=libiproute.a
-ifndef $(LIBIPROUTE_DIR)
-LIBIPROUTE_DIR:=$(TOPDIR)networking/libiproute/
-endif
-
-LIBIPROUTE-$(CONFIG_IP) += \
- ip_parse_common_args.o \
- ipaddress.o \
- iplink.o \
- iproute.o \
- iptunnel.o \
- libnetlink.o \
- ll_addr.o \
- ll_map.o \
- ll_proto.o \
- ll_types.o \
- rt_names.o \
- rtm_map.o \
- utils.o
-
-libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR)
-
-$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
- $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
-
diff --git a/release/src/router/busybox/networking/libiproute/ip_common.h b/release/src/router/busybox/networking/libiproute/ip_common.h
index 25e9c6c8..aef32528 100644
--- a/release/src/router/busybox/networking/libiproute/ip_common.h
+++ b/release/src/router/busybox/networking/libiproute/ip_common.h
@@ -1,18 +1,37 @@
-extern int preferred_family;
-extern char * _SL_;
+/* vi: set sw=4 ts=4: */
+#ifndef IP_COMMON_H
+#define IP_COMMON_H 1
-extern void ip_parse_common_args(int *argcp, char ***argvp);
+#include "libbb.h"
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if !defined IFA_RTA
+#include <linux/if_addr.h>
+#endif
+#if !defined IFLA_RTA
+#include <linux/if_link.h>
+#endif
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+extern char **ip_parse_common_args(char **argv);
extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
-extern int ipaddr_list_or_flush(int argc, char **argv, int flush);
-extern int iproute_monitor(int argc, char **argv);
-extern void iplink_usage(void) __attribute__((noreturn));
+extern int ipaddr_list_or_flush(char **argv, int flush);
+extern int iproute_monitor(char **argv);
+extern void iplink_usage(void) NORETURN;
extern void ipneigh_reset_filter(void);
-extern int do_ipaddr(int argc, char **argv);
-extern int do_iproute(int argc, char **argv);
-extern int do_iprule(int argc, char **argv);
-extern int do_ipneigh(int argc, char **argv);
-extern int do_iptunnel(int argc, char **argv);
-extern int do_iplink(int argc, char **argv);
-extern int do_ipmonitor(int argc, char **argv);
-extern int do_multiaddr(int argc, char **argv);
-extern int do_multiroute(int argc, char **argv);
+
+extern int do_ipaddr(char **argv);
+extern int do_iproute(char **argv);
+extern int do_iprule(char **argv);
+extern int do_ipneigh(char **argv);
+extern int do_iptunnel(char **argv);
+extern int do_iplink(char **argv);
+extern int do_ipmonitor(char **argv);
+extern int do_multiaddr(char **argv);
+extern int do_multiroute(char **argv);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c b/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c
index 21e9f74b..5e4012b8 100644
--- a/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c
+++ b/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* ip.c "ip" utility frontend.
*
@@ -14,61 +15,70 @@
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
*/
-#include <string.h>
-
+#include "ip_common.h" /* #include "libbb.h" is inside */
#include "utils.h"
-#include "ip_common.h"
-
-#include "busybox.h"
-int preferred_family = AF_UNSPEC;
-int oneline = 0;
-char * _SL_ = NULL;
+family_t preferred_family = AF_UNSPEC;
+smallint oneline;
+char _SL_;
-void ip_parse_common_args(int *argcp, char ***argvp)
+char **ip_parse_common_args(char **argv)
{
- int argc = *argcp;
- char **argv = *argvp;
-
- while (argc > 1) {
- char *opt = argv[1];
+ static const char ip_common_commands[] ALIGN1 =
+ "oneline" "\0"
+ "family" "\0"
+ "4" "\0"
+ "6" "\0"
+ "0" "\0"
+ ;
+ enum {
+ ARG_oneline,
+ ARG_family,
+ ARG_IPv4,
+ ARG_IPv6,
+ ARG_packet,
+ };
+ static const family_t af_numbers[] = { AF_INET, AF_INET6, AF_PACKET };
+ int arg;
- if (strcmp(opt,"--") == 0) {
- argc--; argv++;
- break;
- }
+ while (*argv) {
+ char *opt = *argv;
if (opt[0] != '-')
break;
-
- if (opt[1] == '-')
+ opt++;
+ if (opt[0] == '-') {
opt++;
-
- if (matches(opt, "-family") == 0) {
- argc--;
+ if (!opt[0]) { /* "--" */
+ argv++;
+ break;
+ }
+ }
+ arg = index_in_substrings(ip_common_commands, opt);
+ if (arg < 0)
+ bb_show_usage();
+ if (arg == ARG_oneline) {
+ oneline = 1;
argv++;
- if (! argv[1])
- bb_show_usage();
- if (strcmp(argv[1], "inet") == 0)
- preferred_family = AF_INET;
- else if (strcmp(argv[1], "inet6") == 0)
- preferred_family = AF_INET6;
- else if (strcmp(argv[1], "link") == 0)
- preferred_family = AF_PACKET;
- else
- invarg(argv[1], "invalid protocol family");
- } else if (strcmp(opt, "-4") == 0) {
- preferred_family = AF_INET;
- } else if (strcmp(opt, "-6") == 0) {
- preferred_family = AF_INET6;
- } else if (strcmp(opt, "-0") == 0) {
- preferred_family = AF_PACKET;
- } else if (matches(opt, "-oneline") == 0) {
- ++oneline;
+ continue;
+ }
+ if (arg == ARG_family) {
+ static const char families[] ALIGN1 =
+ "inet" "\0" "inet6" "\0" "link" "\0";
+ argv++;
+ if (!*argv)
+ bb_show_usage();
+ arg = index_in_strings(families, *argv);
+ if (arg < 0)
+ invarg(*argv, "protocol family");
+ /* now arg == 0, 1 or 2 */
} else {
- bb_show_usage();
+ arg -= ARG_IPv4;
+ /* now arg == 0, 1 or 2 */
}
- argc--; argv++;
+ preferred_family = af_numbers[arg];
+ argv++;
}
- _SL_ = oneline ? "\\" : "\n" ;
+ _SL_ = oneline ? '\\' : '\n';
+ return argv;
}
diff --git a/release/src/router/busybox/networking/libiproute/ipaddress.c b/release/src/router/busybox/networking/libiproute/ipaddress.c
index 44e871ee..644874f4 100644
--- a/release/src/router/busybox/networking/libiproute/ipaddress.c
+++ b/release/src/router/busybox/networking/libiproute/ipaddress.c
@@ -1,10 +1,8 @@
+/* vi: set sw=4 ts=4: */
/*
* ipaddress.c "ip address".
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
@@ -12,54 +10,50 @@
* Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
*/
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-
#include <fnmatch.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <arpa/inet.h>
-
#include <net/if.h>
#include <net/if_arp.h>
+#include "ip_common.h" /* #include "libbb.h" is inside */
#include "rt_names.h"
#include "utils.h"
-#include "libbb.h"
+#ifndef IFF_LOWER_UP
+/* from linux/if.h */
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up*/
+#endif
-static struct
-{
- int ifindex;
- int family;
- int oneline;
- int showqueue;
- inet_prefix pfx;
- int scope, scopemask;
- int flags, flagmask;
- int up;
+typedef struct filter_t {
char *label;
- int flushed;
char *flushb;
+ struct rtnl_handle *rth;
+ int scope, scopemask;
+ int flags, flagmask;
int flushp;
int flushe;
- struct rtnl_handle *rth;
-} filter;
+ int ifindex;
+ family_t family;
+ smallint showqueue;
+ smallint oneline;
+ smallint up;
+ smallint flushed;
+ inet_prefix pfx;
+} filter_t;
+
+#define filter (*(filter_t*)&bb_common_bufsiz1)
+
-void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+static void print_link_flags(unsigned flags, unsigned mdown)
{
- fprintf(fp, "<");
+ static const int flag_masks[] = {
+ IFF_LOOPBACK, IFF_BROADCAST, IFF_POINTOPOINT,
+ IFF_MULTICAST, IFF_NOARP, IFF_UP, IFF_LOWER_UP };
+ static const char flag_labels[] ALIGN1 =
+ "LOOPBACK\0""BROADCAST\0""POINTOPOINT\0"
+ "MULTICAST\0""NOARP\0""UP\0""LOWER_UP\0";
+
+ bb_putchar('<');
flags &= ~IFF_RUNNING;
-#define _PF(f) if (flags&IFF_##f) { \
- flags &= ~IFF_##f ; \
- fprintf(fp, #f "%s", flags ? "," : ""); }
- _PF(LOOPBACK);
- _PF(BROADCAST);
- _PF(POINTOPOINT);
- _PF(MULTICAST);
- _PF(NOARP);
#if 0
_PF(ALLMULTI);
_PF(PROMISC);
@@ -71,13 +65,12 @@ void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
_PF(PORTSEL);
_PF(NOTRAILERS);
#endif
- _PF(UP);
-#undef _PF
- if (flags)
- fprintf(fp, "%x", flags);
+ flags = print_flags_separated(flag_masks, flag_labels, flags, ",");
+ if (flags)
+ printf("%x", flags);
if (mdown)
- fprintf(fp, ",M-DOWN");
- fprintf(fp, "> ");
+ printf(",M-DOWN");
+ printf("> ");
}
static void print_queuelen(char *name)
@@ -90,9 +83,8 @@ static void print_queuelen(char *name)
return;
memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, name);
- if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
- perror("SIOCGIFXQLEN");
+ strncpy_IFNAMSIZ(ifr.ifr_name, name);
+ if (ioctl_or_warn(s, SIOCGIFTXQLEN, &ifr) < 0) {
close(s);
return;
}
@@ -102,9 +94,8 @@ static void print_queuelen(char *name)
printf("qlen %d", ifr.ifr_qlen);
}
-static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int print_linkinfo(const struct nlmsghdr *n)
{
- FILE *fp = (FILE*)arg;
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct rtattr * tb[IFLA_MAX+1];
int len = n->nlmsg_len;
@@ -119,7 +110,7 @@ static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg
if (filter.ifindex && ifi->ifi_index != filter.ifindex)
return 0;
- if (filter.up && !(ifi->ifi_flags&IFF_UP))
+ if (filter.up && !(ifi->ifi_flags & IFF_UP))
return 0;
memset(tb, 0, sizeof(tb));
@@ -128,85 +119,86 @@ static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg
bb_error_msg("nil ifname");
return -1;
}
- if (filter.label &&
- (!filter.family || filter.family == AF_PACKET) &&
- fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+ if (filter.label
+ && (!filter.family || filter.family == AF_PACKET)
+ && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)
+ ) {
return 0;
+ }
if (n->nlmsg_type == RTM_DELLINK)
- fprintf(fp, "Deleted ");
+ printf("Deleted ");
- fprintf(fp, "%d: %s", ifi->ifi_index,
+ printf("%d: %s", ifi->ifi_index,
tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
if (tb[IFLA_LINK]) {
SPRINT_BUF(b1);
int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
if (iflink == 0)
- fprintf(fp, "@NONE: ");
+ printf("@NONE: ");
else {
- fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+ printf("@%s: ", ll_idx_n2a(iflink, b1));
m_flag = ll_index_to_flags(iflink);
m_flag = !(m_flag & IFF_UP);
}
} else {
- fprintf(fp, ": ");
+ printf(": ");
}
- print_link_flags(fp, ifi->ifi_flags, m_flag);
+ print_link_flags(ifi->ifi_flags, m_flag);
if (tb[IFLA_MTU])
- fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+ printf("mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
if (tb[IFLA_QDISC])
- fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+ printf("qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
#ifdef IFLA_MASTER
if (tb[IFLA_MASTER]) {
SPRINT_BUF(b1);
- fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+ printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
}
#endif
if (filter.showqueue)
print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
-
+
if (!filter.family || filter.family == AF_PACKET) {
SPRINT_BUF(b1);
- fprintf(fp, "%s", _SL_);
- fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+ printf("%c link/%s ", _SL_, ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
if (tb[IFLA_ADDRESS]) {
- fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+ fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
RTA_PAYLOAD(tb[IFLA_ADDRESS]),
ifi->ifi_type,
- b1, sizeof(b1)));
+ b1, sizeof(b1)), stdout);
}
if (tb[IFLA_BROADCAST]) {
- if (ifi->ifi_flags&IFF_POINTOPOINT)
- fprintf(fp, " peer ");
+ if (ifi->ifi_flags & IFF_POINTOPOINT)
+ printf(" peer ");
else
- fprintf(fp, " brd ");
- fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+ printf(" brd ");
+ fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
RTA_PAYLOAD(tb[IFLA_BROADCAST]),
ifi->ifi_type,
- b1, sizeof(b1)));
+ b1, sizeof(b1)), stdout);
}
}
- fprintf(fp, "\n");
- fflush(fp);
+ bb_putchar('\n');
+ /*fflush(stdout);*/
return 0;
}
static int flush_update(void)
{
if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
- perror("Failed to send flush request\n");
+ bb_perror_msg("failed to send flush request");
return -1;
}
filter.flushp = 0;
return 0;
}
-static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
{
- FILE *fp = (FILE*)arg;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr * rta_tb[IFA_MAX+1];
@@ -234,9 +226,9 @@ static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg
if (filter.ifindex && filter.ifindex != ifa->ifa_index)
return 0;
- if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+ if ((filter.scope ^ ifa->ifa_scope) & filter.scopemask)
return 0;
- if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+ if ((filter.flags ^ ifa->ifa_flags) & filter.flagmask)
return 0;
if (filter.label) {
const char *label;
@@ -270,35 +262,34 @@ static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg
fn->nlmsg_flags = NLM_F_REQUEST;
fn->nlmsg_seq = ++filter.rth->seq;
filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
- filter.flushed++;
+ filter.flushed = 1;
return 0;
}
if (n->nlmsg_type == RTM_DELADDR)
- fprintf(fp, "Deleted ");
+ printf("Deleted ");
if (filter.oneline)
- fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+ printf("%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
if (ifa->ifa_family == AF_INET)
- fprintf(fp, " inet ");
+ printf(" inet ");
else if (ifa->ifa_family == AF_INET6)
- fprintf(fp, " inet6 ");
+ printf(" inet6 ");
else
- fprintf(fp, " family %d ", ifa->ifa_family);
+ printf(" family %d ", ifa->ifa_family);
if (rta_tb[IFA_LOCAL]) {
- fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
- RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+ fputs(rt_addr_n2a(ifa->ifa_family,
RTA_DATA(rta_tb[IFA_LOCAL]),
- abuf, sizeof(abuf)));
+ abuf, sizeof(abuf)), stdout);
- if (rta_tb[IFA_ADDRESS] == NULL ||
- memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
- fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+ if (rta_tb[IFA_ADDRESS] == NULL
+ || memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0
+ ) {
+ printf("/%d ", ifa->ifa_prefixlen);
} else {
- fprintf(fp, " peer %s/%d ",
+ printf(" peer %s/%d ",
rt_addr_n2a(ifa->ifa_family,
- RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
RTA_DATA(rta_tb[IFA_ADDRESS]),
abuf, sizeof(abuf)),
ifa->ifa_prefixlen);
@@ -306,44 +297,42 @@ static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg
}
if (rta_tb[IFA_BROADCAST]) {
- fprintf(fp, "brd %s ",
+ printf("brd %s ",
rt_addr_n2a(ifa->ifa_family,
- RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
RTA_DATA(rta_tb[IFA_BROADCAST]),
abuf, sizeof(abuf)));
}
if (rta_tb[IFA_ANYCAST]) {
- fprintf(fp, "any %s ",
+ printf("any %s ",
rt_addr_n2a(ifa->ifa_family,
- RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
RTA_DATA(rta_tb[IFA_ANYCAST]),
abuf, sizeof(abuf)));
}
- fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
- if (ifa->ifa_flags&IFA_F_SECONDARY) {
+ printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+ if (ifa->ifa_flags & IFA_F_SECONDARY) {
ifa->ifa_flags &= ~IFA_F_SECONDARY;
- fprintf(fp, "secondary ");
+ printf("secondary ");
}
- if (ifa->ifa_flags&IFA_F_TENTATIVE) {
+ if (ifa->ifa_flags & IFA_F_TENTATIVE) {
ifa->ifa_flags &= ~IFA_F_TENTATIVE;
- fprintf(fp, "tentative ");
+ printf("tentative ");
}
- if (ifa->ifa_flags&IFA_F_DEPRECATED) {
+ if (ifa->ifa_flags & IFA_F_DEPRECATED) {
ifa->ifa_flags &= ~IFA_F_DEPRECATED;
- fprintf(fp, "deprecated ");
+ printf("deprecated ");
}
- if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
- fprintf(fp, "dynamic ");
+ if (!(ifa->ifa_flags & IFA_F_PERMANENT)) {
+ printf("dynamic ");
} else
ifa->ifa_flags &= ~IFA_F_PERMANENT;
if (ifa->ifa_flags)
- fprintf(fp, "flags %02x ", ifa->ifa_flags);
+ printf("flags %02x ", ifa->ifa_flags);
if (rta_tb[IFA_LABEL])
- fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+ fputs((char*)RTA_DATA(rta_tb[IFA_LABEL]), stdout);
if (rta_tb[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
char buf[128];
- fprintf(fp, "%s", _SL_);
+ bb_putchar(_SL_);
if (ci->ifa_valid == 0xFFFFFFFFU)
sprintf(buf, "valid_lft forever");
else
@@ -352,10 +341,10 @@ static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg
sprintf(buf+strlen(buf), " preferred_lft forever");
else
sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
- fprintf(fp, " %s", buf);
+ printf(" %s", buf);
}
- fprintf(fp, "\n");
- fflush(fp);
+ bb_putchar('\n');
+ /*fflush(stdout);*/
return 0;
}
@@ -366,9 +355,9 @@ struct nlmsg_list
struct nlmsghdr h;
};
-static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo)
{
- for ( ;ainfo ; ainfo = ainfo->next) {
+ for (; ainfo; ainfo = ainfo->next) {
struct nlmsghdr *n = &ainfo->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
@@ -378,17 +367,17 @@ static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
return -1;
- if (ifa->ifa_index != ifindex ||
+ if (ifa->ifa_index != ifindex ||
(filter.family && filter.family != ifa->ifa_family))
continue;
- print_addrinfo(NULL, n, fp);
+ print_addrinfo(NULL, n, NULL);
}
return 0;
}
-static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
{
struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
struct nlmsg_list *h;
@@ -401,7 +390,8 @@ static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
memcpy(&h->h, n, n->nlmsg_len);
h->next = NULL;
- for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
+ for (lp = linfo; *lp; lp = &(*lp)->next)
+ continue;
*lp = h;
ll_remember_index(who, n, NULL);
@@ -414,9 +404,11 @@ static void ipaddr_reset_filter(int _oneline)
filter.oneline = _oneline;
}
-extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
+/* Return value becomes exitcode. It's okay to not return at all */
+int ipaddr_list_or_flush(char **argv, int flush)
{
- const char *option[] = { "to", "scope", "up", "label", "dev", 0 };
+ static const char option[] ALIGN1 = "to\0""scope\0""up\0""label\0""dev\0";
+
struct nlmsg_list *linfo = NULL;
struct nlmsg_list *ainfo = NULL;
struct nlmsg_list *l;
@@ -431,18 +423,16 @@ extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
filter.family = preferred_family;
if (flush) {
- if (argc <= 0) {
- fprintf(stderr, "Flush requires arguments.\n");
- return -1;
+ if (!*argv) {
+ bb_error_msg_and_die(bb_msg_requires_arg, "flush");
}
if (filter.family == AF_PACKET) {
- fprintf(stderr, "Cannot flush link addresses.\n");
- return -1;
+ bb_error_msg_and_die("cannot flush link addresses");
}
}
- while (argc > 0) {
- const unsigned short option_num = compare_string_array(option, *argv);
+ while (*argv) {
+ const int option_num = index_in_strings(option, *argv);
switch (option_num) {
case 0: /* to */
NEXT_ARG();
@@ -453,12 +443,12 @@ extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
break;
case 1: /* scope */
{
- int scope = 0;
+ uint32_t scope = 0;
NEXT_ARG();
filter.scopemask = -1;
if (rtnl_rtscope_a2n(&scope, *argv)) {
if (strcmp(*argv, "all") != 0) {
- invarg("invalid \"scope\"\n", *argv);
+ invarg(*argv, "scope");
}
scope = RT_SCOPE_NOWHERE;
filter.scopemask = 0;
@@ -482,30 +472,18 @@ extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
filter_dev = *argv;
}
argv++;
- argc--;
}
- if (rtnl_open(&rth, 0) < 0)
- exit(1);
+ xrtnl_open(&rth);
- if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
- bb_perror_msg_and_die("Cannot send dump request");
- }
-
- if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
- bb_error_msg_and_die("Dump terminated");
- }
+ xrtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK);
+ xrtnl_dump_filter(&rth, store_nlmsg, &linfo);
if (filter_dev) {
- filter.ifindex = ll_name_to_index(filter_dev);
- if (filter.ifindex <= 0) {
- bb_error_msg("Device \"%s\" does not exist.", filter_dev);
- return -1;
- }
+ filter.ifindex = xll_name_to_index(filter_dev);
}
if (flush) {
- int round = 0;
char flushb[4096-512];
filter.flushb = flushb;
@@ -514,62 +492,45 @@ extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
filter.rth = &rth;
for (;;) {
- if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
- perror("Cannot send dump request");
- exit(1);
- }
+ xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
filter.flushed = 0;
- if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
- fprintf(stderr, "Flush terminated\n");
- exit(1);
- }
+ xrtnl_dump_filter(&rth, print_addrinfo, NULL);
if (filter.flushed == 0) {
-#if 0
- if (round == 0)
- fprintf(stderr, "Nothing to flush.\n");
-#endif
- fflush(stdout);
return 0;
}
- round++;
if (flush_update() < 0)
- exit(1);
+ return 1;
}
}
if (filter.family != AF_PACKET) {
- if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
- bb_perror_msg_and_die("Cannot send dump request");
- }
-
- if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
- bb_error_msg_and_die("Dump terminated");
- }
+ xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
+ xrtnl_dump_filter(&rth, store_nlmsg, &ainfo);
}
if (filter.family && filter.family != AF_PACKET) {
struct nlmsg_list **lp;
- lp=&linfo;
+ lp = &linfo;
if (filter.oneline)
no_link = 1;
- while ((l=*lp)!=NULL) {
+ while ((l = *lp) != NULL) {
int ok = 0;
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
struct nlmsg_list *a;
- for (a=ainfo; a; a=a->next) {
+ for (a = ainfo; a; a = a->next) {
struct nlmsghdr *n = &a->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
- if (ifa->ifa_index != ifi->ifi_index ||
+ if (ifa->ifa_index != ifi->ifi_index ||
(filter.family && filter.family != ifa->ifa_family))
continue;
- if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+ if ((filter.scope ^ ifa->ifa_scope) & filter.scopemask)
continue;
- if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+ if ((filter.flags ^ ifa->ifa_flags) & filter.flagmask)
continue;
if (filter.pfx.family || filter.label) {
struct rtattr *tb[IFA_MAX+1];
@@ -608,46 +569,47 @@ extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
}
}
- for (l=linfo; l; l = l->next) {
- if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
+ for (l = linfo; l; l = l->next) {
+ if (no_link || print_linkinfo(&l->h) == 0) {
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
if (filter.family != AF_PACKET)
- print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
+ print_selected_addrinfo(ifi->ifi_index, ainfo);
}
- fflush(stdout);
}
- exit(0);
+ return 0;
}
static int default_scope(inet_prefix *lcl)
{
if (lcl->family == AF_INET) {
- if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
+ if (lcl->bytelen >= 1 && *(uint8_t*)&lcl->data == 127)
return RT_SCOPE_HOST;
}
return 0;
}
-static int ipaddr_modify(int cmd, int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int ipaddr_modify(int cmd, char **argv)
{
- const char *option[] = { "peer", "remote", "broadcast", "brd",
- "anycast", "scope", "dev", "label", "local", 0 };
+ static const char option[] ALIGN1 =
+ "peer\0""remote\0""broadcast\0""brd\0"
+ "anycast\0""scope\0""dev\0""label\0""local\0";
struct rtnl_handle rth;
struct {
- struct nlmsghdr n;
- struct ifaddrmsg ifa;
- char buf[256];
+ struct nlmsghdr n;
+ struct ifaddrmsg ifa;
+ char buf[256];
} req;
- char *d = NULL;
- char *l = NULL;
+ char *d = NULL;
+ char *l = NULL;
inet_prefix lcl;
inet_prefix peer;
int local_len = 0;
int peer_len = 0;
int brd_len = 0;
int any_len = 0;
- int scoped = 0;
+ bool scoped = 0;
memset(&req, 0, sizeof(req));
@@ -656,8 +618,8 @@ static int ipaddr_modify(int cmd, int argc, char **argv)
req.n.nlmsg_type = cmd;
req.ifa.ifa_family = preferred_family;
- while (argc > 0) {
- const unsigned short option_num = compare_string_array(option, *argv);
+ while (*argv) {
+ const int option_num = index_in_strings(option, *argv);
switch (option_num) {
case 0: /* peer */
case 1: /* remote */
@@ -682,10 +644,9 @@ static int ipaddr_modify(int cmd, int argc, char **argv)
if (brd_len) {
duparg("broadcast", *argv);
}
- if (strcmp(*argv, "+") == 0) {
+ if (LONE_CHAR(*argv, '+')) {
brd_len = -1;
- }
- else if (strcmp(*argv, "-") == 0) {
+ } else if (LONE_DASH(*argv)) {
brd_len = -2;
} else {
get_addr(&addr, *argv, req.ifa.ifa_family);
@@ -713,10 +674,10 @@ static int ipaddr_modify(int cmd, int argc, char **argv)
}
case 5: /* scope */
{
- int scope = 0;
+ uint32_t scope = 0;
NEXT_ARG();
if (rtnl_rtscope_a2n(&scope, *argv)) {
- invarg(*argv, "invalid scope value.");
+ invarg(*argv, "scope");
}
req.ifa.ifa_scope = scope;
scoped = 1;
@@ -744,16 +705,15 @@ static int ipaddr_modify(int cmd, int argc, char **argv)
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
local_len = lcl.bytelen;
}
- argc--;
argv++;
}
if (d == NULL) {
- bb_error_msg("Not enough information: \"dev\" argument is required.");
+ bb_error_msg(bb_msg_requires_arg, "\"dev\"");
return -1;
}
- if (l && matches(d, l) != 0) {
- bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l);
+ if (l && strncmp(d, l, strlen(d)) != 0) {
+ bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
}
if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
@@ -767,8 +727,7 @@ static int ipaddr_modify(int cmd, int argc, char **argv)
inet_prefix brd;
int i;
if (req.ifa.ifa_family != AF_INET) {
- bb_error_msg("Broadcast can be set only for IPv4 addresses");
- return -1;
+ bb_error_msg_and_die("broadcast can be set only for IPv4 addresses");
}
brd = peer;
if (brd.bitlen <= 30) {
@@ -785,41 +744,38 @@ static int ipaddr_modify(int cmd, int argc, char **argv)
if (!scoped && cmd != RTM_DELADDR)
req.ifa.ifa_scope = default_scope(&lcl);
- if (rtnl_open(&rth, 0) < 0)
- exit(1);
+ xrtnl_open(&rth);
ll_init_map(&rth);
- if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
- bb_error_msg("Cannot find device \"%s\"", d);
- return -1;
- }
+ req.ifa.ifa_index = xll_name_to_index(d);
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
- exit(2);
+ return 2;
- exit(0);
+ return 0;
}
-extern int do_ipaddr(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_ipaddr(char **argv)
{
- const char *commands[] = { "add", "delete", "list", "show", "lst", "flush", 0 };
- unsigned short command_num = 2;
+ static const char commands[] ALIGN1 =
+ "add\0""delete\0""list\0""show\0""lst\0""flush\0";
+
+ int command_num = 2; /* default command is list */
if (*argv) {
- command_num = compare_string_array(commands, *argv);
- }
- switch (command_num) {
- case 0: /* add */
- return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
- case 1: /* delete */
- return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
- case 2: /* list */
- case 3: /* show */
- case 4: /* lst */
- return ipaddr_list_or_flush(argc-1, argv+1, 0);
- case 5: /* flush */
- return ipaddr_list_or_flush(argc-1, argv+1, 1);
- }
- bb_error_msg_and_die("Unknown command %s", *argv);
+ command_num = index_in_substrings(commands, *argv);
+ if (command_num < 0 || command_num > 5)
+ bb_error_msg_and_die("unknown command %s", *argv);
+ argv++;
+ }
+ if (command_num == 0) /* add */
+ return ipaddr_modify(RTM_NEWADDR, argv);
+ if (command_num == 1) /* delete */
+ return ipaddr_modify(RTM_DELADDR, argv);
+ if (command_num == 5) /* flush */
+ return ipaddr_list_or_flush(argv, 1);
+ /* 2 == list, 3 == show, 4 == lst */
+ return ipaddr_list_or_flush(argv, 0);
}
diff --git a/release/src/router/busybox/networking/libiproute/iplink.c b/release/src/router/busybox/networking/libiproute/iplink.c
index daab6938..1e7ee07d 100644
--- a/release/src/router/busybox/networking/libiproute/iplink.c
+++ b/release/src/router/busybox/networking/libiproute/iplink.c
@@ -1,248 +1,171 @@
+/* vi: set sw=4 ts=4: */
/*
- * iplink.c "ip link".
+ * iplink.c "ip link".
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <linux/version.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
+//#include <sys/ioctl.h>
+//#include <sys/socket.h>
#include <net/if.h>
#include <net/if_packet.h>
#include <netpacket/packet.h>
-
-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <net/ethernet.h>
-#else
-#include <linux/if_ether.h>
-#endif
+#include "ip_common.h" /* #include "libbb.h" is inside */
#include "rt_names.h"
#include "utils.h"
-#include "ip_common.h"
-
-#include "libbb.h"
-
-/* take from linux/sockios.h */
+/* taken from linux/sockios.h */
#define SIOCSIFNAME 0x8923 /* set interface name */
-static int do_link;
-
-static int on_off(char *msg)
-{
- bb_error_msg("Error: argument of \"%s\" must be \"on\" or \"off\"", msg);
- return -1;
-}
-
+/* Exits on error */
static int get_ctl_fd(void)
{
- int s_errno;
int fd;
fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd >= 0)
return fd;
- s_errno = errno;
fd = socket(PF_PACKET, SOCK_DGRAM, 0);
if (fd >= 0)
return fd;
- fd = socket(PF_INET6, SOCK_DGRAM, 0);
- if (fd >= 0)
- return fd;
- errno = s_errno;
- perror("Cannot create control socket");
- return -1;
+ return xsocket(PF_INET6, SOCK_DGRAM, 0);
}
-static int do_chflags(char *dev, __u32 flags, __u32 mask)
+/* Exits on error */
+static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
{
struct ifreq ifr;
int fd;
- int err;
- strcpy(ifr.ifr_name, dev);
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
fd = get_ctl_fd();
- if (fd < 0)
- return -1;
- err = ioctl(fd, SIOCGIFFLAGS, &ifr);
- if (err) {
- perror("SIOCGIFFLAGS");
- close(fd);
- return -1;
- }
- if ((ifr.ifr_flags^flags)&mask) {
+ xioctl(fd, SIOCGIFFLAGS, &ifr);
+ if ((ifr.ifr_flags ^ flags) & mask) {
ifr.ifr_flags &= ~mask;
- ifr.ifr_flags |= mask&flags;
- err = ioctl(fd, SIOCSIFFLAGS, &ifr);
- if (err)
- perror("SIOCSIFFLAGS");
+ ifr.ifr_flags |= mask & flags;
+ xioctl(fd, SIOCSIFFLAGS, &ifr);
}
close(fd);
- return err;
}
-static int do_changename(char *dev, char *newdev)
+/* Exits on error */
+static void do_changename(char *dev, char *newdev)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
struct ifreq ifr;
int fd;
- int err;
- strcpy(ifr.ifr_name, dev);
- strcpy(ifr.ifr_newname, newdev);
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+ strncpy_IFNAMSIZ(ifr.ifr_newname, newdev);
fd = get_ctl_fd();
- if (fd < 0)
- return -1;
- err = ioctl(fd, SIOCSIFNAME, &ifr);
- if (err) {
- perror("SIOCSIFNAME");
- close(fd);
- return -1;
- }
+ xioctl(fd, SIOCSIFNAME, &ifr);
close(fd);
- return err;
-#endif
- return 0;
}
-static int set_qlen(char *dev, int qlen)
+/* Exits on error */
+static void set_qlen(char *dev, int qlen)
{
struct ifreq ifr;
int s;
s = get_ctl_fd();
- if (s < 0)
- return -1;
-
memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, dev);
- ifr.ifr_qlen = qlen;
- if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
- perror("SIOCSIFXQLEN");
- close(s);
- return -1;
- }
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+ ifr.ifr_qlen = qlen;
+ xioctl(s, SIOCSIFTXQLEN, &ifr);
close(s);
-
- return 0;
}
-static int set_mtu(char *dev, int mtu)
+/* Exits on error */
+static void set_mtu(char *dev, int mtu)
{
struct ifreq ifr;
int s;
s = get_ctl_fd();
- if (s < 0)
- return -1;
-
memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, dev);
- ifr.ifr_mtu = mtu;
- if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
- perror("SIOCSIFMTU");
- close(s);
- return -1;
- }
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+ ifr.ifr_mtu = mtu;
+ xioctl(s, SIOCSIFMTU, &ifr);
close(s);
-
- return 0;
}
+/* Exits on error */
static int get_address(char *dev, int *htype)
{
struct ifreq ifr;
struct sockaddr_ll me;
- int alen;
+ socklen_t alen;
int s;
- s = socket(PF_PACKET, SOCK_DGRAM, 0);
- if (s < 0) {
- perror("socket(PF_PACKET)");
- return -1;
- }
+ s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, dev);
- if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
- perror("SIOCGIFINDEX");
- close(s);
- return -1;
- }
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+ xioctl(s, SIOCGIFINDEX, &ifr);
memset(&me, 0, sizeof(me));
me.sll_family = AF_PACKET;
me.sll_ifindex = ifr.ifr_ifindex;
me.sll_protocol = htons(ETH_P_LOOP);
- if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
- perror("bind");
- close(s);
- return -1;
- }
+ xbind(s, (struct sockaddr*)&me, sizeof(me));
alen = sizeof(me);
if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
- perror("getsockname");
- close(s);
- return -1;
+ bb_perror_msg_and_die("getsockname");
}
close(s);
*htype = me.sll_hatype;
return me.sll_halen;
}
-static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+/* Exits on error */
+static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
{
int alen;
memset(ifr, 0, sizeof(*ifr));
- strcpy(ifr->ifr_name, dev);
+ strncpy_IFNAMSIZ(ifr->ifr_name, dev);
ifr->ifr_hwaddr.sa_family = hatype;
- alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
+
+ alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
+ alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla);
if (alen < 0)
- return -1;
+ exit(EXIT_FAILURE);
if (alen != halen) {
- bb_error_msg("Wrong address (%s) length: expected %d bytes", lla, halen);
- return -1;
+ bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
}
- return 0;
}
-static int set_address(struct ifreq *ifr, int brd)
+/* Exits on error */
+static void set_address(struct ifreq *ifr, int brd)
{
int s;
s = get_ctl_fd();
- if (s < 0)
- return -1;
- if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
- perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
- close(s);
- return -1;
- }
+ if (brd)
+ xioctl(s, SIOCSIFHWBROADCAST, ifr);
+ else
+ xioctl(s, SIOCSIFHWADDR, ifr);
close(s);
- return 0;
}
-static int do_set(int argc, char **argv)
+static void die_must_be_on_off(const char *msg) NORETURN;
+static void die_must_be_on_off(const char *msg)
+{
+ bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg);
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_set(char **argv)
{
char *dev = NULL;
- __u32 mask = 0;
- __u32 flags = 0;
+ uint32_t mask = 0;
+ uint32_t flags = 0;
int qlen = -1;
int mtu = -1;
char *newaddr = NULL;
@@ -250,118 +173,133 @@ static int do_set(int argc, char **argv)
struct ifreq ifr0, ifr1;
char *newname = NULL;
int htype, halen;
-
- while (argc > 0) {
- if (strcmp(*argv, "up") == 0) {
+ static const char keywords[] ALIGN1 =
+ "up\0""down\0""name\0""mtu\0""multicast\0"
+ "arp\0""address\0""dev\0";
+ enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_multicast,
+ ARG_arp, ARG_addr, ARG_dev };
+ static const char str_on_off[] ALIGN1 = "on\0""off\0";
+ enum { PARM_on = 0, PARM_off };
+ smalluint key;
+
+ while (*argv) {
+ /* substring search ensures that e.g. "addr" and "address"
+ * are both accepted */
+ key = index_in_substrings(keywords, *argv);
+ if (key == ARG_up) {
mask |= IFF_UP;
flags |= IFF_UP;
- } else if (strcmp(*argv, "down") == 0) {
+ }
+ if (key == ARG_down) {
mask |= IFF_UP;
flags &= ~IFF_UP;
- } else if (strcmp(*argv, "name") == 0) {
+ }
+ if (key == ARG_name) {
NEXT_ARG();
newname = *argv;
- } else if (strcmp(*argv, "mtu") == 0) {
+ }
+ if (key == ARG_mtu) {
NEXT_ARG();
if (mtu != -1)
duparg("mtu", *argv);
- if (get_integer(&mtu, *argv, 0))
- invarg("Invalid \"mtu\" value\n", *argv);
- } else if (strcmp(*argv, "multicast") == 0) {
+ mtu = get_unsigned(*argv, "mtu");
+ }
+ if (key == ARG_multicast) {
+ int param;
NEXT_ARG();
mask |= IFF_MULTICAST;
- if (strcmp(*argv, "on") == 0) {
+ param = index_in_strings(str_on_off, *argv);
+ if (param < 0)
+ die_must_be_on_off("multicast");
+ if (param == PARM_on)
flags |= IFF_MULTICAST;
- } else if (strcmp(*argv, "off") == 0) {
+ else
flags &= ~IFF_MULTICAST;
- } else
- return on_off("multicast");
- } else if (strcmp(*argv, "arp") == 0) {
+ }
+ if (key == ARG_arp) {
+ int param;
NEXT_ARG();
mask |= IFF_NOARP;
- if (strcmp(*argv, "on") == 0) {
+ param = index_in_strings(str_on_off, *argv);
+ if (param < 0)
+ die_must_be_on_off("arp");
+ if (param == PARM_on)
flags &= ~IFF_NOARP;
- } else if (strcmp(*argv, "off") == 0) {
+ else
flags |= IFF_NOARP;
- } else
- return on_off("noarp");
- } else {
- if (strcmp(*argv, "dev") == 0) {
+ }
+ if (key == ARG_addr) {
+ NEXT_ARG();
+ newaddr = *argv;
+ }
+ if (key >= ARG_dev) {
+ if (key == ARG_dev) {
NEXT_ARG();
}
if (dev)
duparg2("dev", *argv);
dev = *argv;
}
- argc--; argv++;
+ argv++;
}
if (!dev) {
- bb_error_msg("Not enough of information: \"dev\" argument is required.");
- exit(-1);
+ bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
}
if (newaddr || newbrd) {
halen = get_address(dev, &htype);
- if (halen < 0)
- return -1;
if (newaddr) {
- if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
- return -1;
+ parse_address(dev, htype, halen, newaddr, &ifr0);
}
if (newbrd) {
- if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
- return -1;
+ parse_address(dev, htype, halen, newbrd, &ifr1);
}
}
if (newname && strcmp(dev, newname)) {
- if (do_changename(dev, newname) < 0)
- return -1;
+ do_changename(dev, newname);
dev = newname;
}
- if (qlen != -1) {
- if (set_qlen(dev, qlen) < 0)
- return -1;
+ if (qlen != -1) {
+ set_qlen(dev, qlen);
}
- if (mtu != -1) {
- if (set_mtu(dev, mtu) < 0)
- return -1;
+ if (mtu != -1) {
+ set_mtu(dev, mtu);
}
if (newaddr || newbrd) {
if (newbrd) {
- if (set_address(&ifr1, 1) < 0)
- return -1;
+ set_address(&ifr1, 1);
}
if (newaddr) {
- if (set_address(&ifr0, 0) < 0)
- return -1;
+ set_address(&ifr0, 0);
}
}
if (mask)
- return do_chflags(dev, flags, mask);
+ do_chflags(dev, flags, mask);
return 0;
}
-static int ipaddr_list_link(int argc, char **argv)
+static int ipaddr_list_link(char **argv)
{
preferred_family = AF_PACKET;
- do_link = 1;
- return ipaddr_list_or_flush(argc, argv, 0);
+ return ipaddr_list_or_flush(argv, 0);
}
-int do_iplink(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iplink(char **argv)
{
- if (argc > 0) {
- if (matches(*argv, "set") == 0)
- return do_set(argc-1, argv+1);
- if (matches(*argv, "show") == 0 ||
- matches(*argv, "lst") == 0 ||
- matches(*argv, "list") == 0)
- return ipaddr_list_link(argc-1, argv+1);
- } else
- return ipaddr_list_link(0, NULL);
-
- bb_error_msg("Command \"%s\" is unknown, try \"ip link help\".", *argv);
- exit(-1);
+ static const char keywords[] ALIGN1 =
+ "set\0""show\0""lst\0""list\0";
+ int key;
+ if (!*argv)
+ return ipaddr_list_link(argv);
+ key = index_in_substrings(keywords, *argv);
+ if (key < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ argv++;
+ if (key == 0) /* set */
+ return do_set(argv);
+ /* show, lst, list */
+ return ipaddr_list_link(argv);
}
diff --git a/release/src/router/busybox/networking/libiproute/iproute.c b/release/src/router/busybox/networking/libiproute/iproute.c
index 70ddf9f4..66557d8f 100644
--- a/release/src/router/busybox/networking/libiproute/iproute.c
+++ b/release/src/router/busybox/networking/libiproute/iproute.c
@@ -1,10 +1,8 @@
+/* vi: set sw=4 ts=4: */
/*
* iproute.c "ip route".
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
@@ -15,27 +13,18 @@
* Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
*/
-#include <sys/socket.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-
+#include "ip_common.h" /* #include "libbb.h" is inside */
#include "rt_names.h"
#include "utils.h"
-#include "libbb.h"
-
#ifndef RTAX_RTTVAR
#define RTAX_RTTVAR RTAX_HOPS
#endif
-static struct
-{
+typedef struct filter_t {
int tb;
- int flushed;
+ smallint flushed;
char *flushb;
int flushp;
int flushe;
@@ -53,21 +42,45 @@ static struct
inet_prefix mdst;
inet_prefix rsrc;
inet_prefix msrc;
-} filter;
+} filter_t;
+
+#define filter (*(filter_t*)&bb_common_bufsiz1)
static int flush_update(void)
{
if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
- perror("Failed to send flush request\n");
+ bb_perror_msg("failed to send flush request");
return -1;
}
filter.flushp = 0;
return 0;
}
-static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static unsigned get_hz(void)
+{
+ static unsigned hz_internal;
+ FILE *fp;
+
+ if (hz_internal)
+ return hz_internal;
+
+ fp = fopen_for_read("/proc/net/psched");
+ if (fp) {
+ unsigned nom, denom;
+
+ if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+ if (nom == 1000000)
+ hz_internal = denom;
+ fclose(fp);
+ }
+ if (!hz_internal)
+ hz_internal = sysconf(_SC_CLK_TCK);
+ return hz_internal;
+}
+
+static int print_route(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
{
- FILE *fp = (FILE*)arg;
struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr * tb[RTA_MAX+1];
@@ -76,7 +89,6 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
inet_prefix src;
int host_len = -1;
SPRINT_BUF(b1);
-
if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
fprintf(stderr, "Not a route: %08x %08x %08x\n",
@@ -86,10 +98,8 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
return 0;
len -= NLMSG_LENGTH(sizeof(*r));
- if (len < 0) {
- bb_error_msg("wrong nlmsg len %d", len);
- return -1;
- }
+ if (len < 0)
+ bb_error_msg_and_die("wrong nlmsg len %d", len);
if (r->rtm_family == AF_INET6)
host_len = 128;
@@ -99,11 +109,11 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
if (r->rtm_family == AF_INET6) {
if (filter.tb) {
if (filter.tb < 0) {
- if (!(r->rtm_flags&RTM_F_CLONED)) {
+ if (!(r->rtm_flags & RTM_F_CLONED)) {
return 0;
}
} else {
- if (r->rtm_flags&RTM_F_CLONED) {
+ if (r->rtm_flags & RTM_F_CLONED) {
return 0;
}
if (filter.tb == RT_TABLE_LOCAL) {
@@ -170,7 +180,7 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
struct nlmsghdr *fn;
if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
if (flush_update())
- return -1;
+ bb_error_msg_and_die("flush");
}
fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
memcpy(fn, n, n->nlmsg_len);
@@ -178,78 +188,73 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
fn->nlmsg_flags = NLM_F_REQUEST;
fn->nlmsg_seq = ++filter.rth->seq;
filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
- filter.flushed++;
+ filter.flushed = 1;
return 0;
}
if (n->nlmsg_type == RTM_DELROUTE) {
- fprintf(fp, "Deleted ");
+ printf("Deleted ");
}
if (r->rtm_type != RTN_UNICAST && !filter.type) {
- fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+ printf("%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
}
if (tb[RTA_DST]) {
if (r->rtm_dst_len != host_len) {
- fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
- RTA_PAYLOAD(tb[RTA_DST]),
- RTA_DATA(tb[RTA_DST]),
- abuf, sizeof(abuf)),
- r->rtm_dst_len
- );
+ printf("%s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)),
+ r->rtm_dst_len
+ );
} else {
- fprintf(fp, "%s ", format_host(r->rtm_family,
- RTA_PAYLOAD(tb[RTA_DST]),
- RTA_DATA(tb[RTA_DST]),
- abuf, sizeof(abuf))
- );
+ printf("%s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf))
+ );
}
} else if (r->rtm_dst_len) {
- fprintf(fp, "0/%d ", r->rtm_dst_len);
+ printf("0/%d ", r->rtm_dst_len);
} else {
- fprintf(fp, "default ");
+ printf("default ");
}
if (tb[RTA_SRC]) {
if (r->rtm_src_len != host_len) {
- fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
- RTA_PAYLOAD(tb[RTA_SRC]),
- RTA_DATA(tb[RTA_SRC]),
- abuf, sizeof(abuf)),
- r->rtm_src_len
- );
+ printf("from %s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)),
+ r->rtm_src_len
+ );
} else {
- fprintf(fp, "from %s ", format_host(r->rtm_family,
- RTA_PAYLOAD(tb[RTA_SRC]),
- RTA_DATA(tb[RTA_SRC]),
- abuf, sizeof(abuf))
- );
+ printf("from %s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf))
+ );
}
} else if (r->rtm_src_len) {
- fprintf(fp, "from 0/%u ", r->rtm_src_len);
+ printf("from 0/%u ", r->rtm_src_len);
}
if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
- fprintf(fp, "via %s ",
- format_host(r->rtm_family,
- RTA_PAYLOAD(tb[RTA_GATEWAY]),
- RTA_DATA(tb[RTA_GATEWAY]),
- abuf, sizeof(abuf)));
+ printf("via %s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
}
if (tb[RTA_OIF] && filter.oifmask != -1) {
- fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
+ printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
}
if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
/* Do not use format_host(). It is our local addr
and symbolic name will not be useful.
*/
- fprintf(fp, " src %s ",
- rt_addr_n2a(r->rtm_family,
- RTA_PAYLOAD(tb[RTA_PREFSRC]),
- RTA_DATA(tb[RTA_PREFSRC]),
- abuf, sizeof(abuf)));
+ printf(" src %s ", rt_addr_n2a(r->rtm_family,
+ RTA_DATA(tb[RTA_PREFSRC]),
+ abuf, sizeof(abuf)));
}
if (tb[RTA_PRIORITY]) {
- fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
+ printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
}
if (r->rtm_family == AF_INET6) {
struct rta_cacheinfo *ci = NULL;
@@ -257,57 +262,73 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
ci = RTA_DATA(tb[RTA_CACHEINFO]);
}
if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
- static int hz;
- if (!hz) {
- hz = get_hz();
- }
if (r->rtm_flags & RTM_F_CLONED) {
- fprintf(fp, "%s cache ", _SL_);
+ printf("%c cache ", _SL_);
}
if (ci->rta_expires) {
- fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+ printf(" expires %dsec", ci->rta_expires / get_hz());
}
if (ci->rta_error != 0) {
- fprintf(fp, " error %d", ci->rta_error);
+ printf(" error %d", ci->rta_error);
}
} else if (ci) {
if (ci->rta_error != 0)
- fprintf(fp, " error %d", ci->rta_error);
+ printf(" error %d", ci->rta_error);
}
}
if (tb[RTA_IIF] && filter.iifmask != -1) {
- fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+ printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
}
- fprintf(fp, "\n");
- fflush(fp);
+ bb_putchar('\n');
return 0;
}
-static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_modify(int cmd, unsigned flags, char **argv)
{
+ static const char keywords[] ALIGN1 =
+ "src\0""via\0""mtu\0""lock\0""protocol\0"USE_FEATURE_IP_RULE("table\0")
+ "dev\0""oif\0""to\0""metric\0";
+ enum {
+ ARG_src,
+ ARG_via,
+ ARG_mtu, PARM_lock,
+ ARG_protocol,
+USE_FEATURE_IP_RULE(ARG_table,)
+ ARG_dev,
+ ARG_oif,
+ ARG_to,
+ ARG_metric,
+ };
+ enum {
+ gw_ok = 1 << 0,
+ dst_ok = 1 << 1,
+ proto_ok = 1 << 2,
+ type_ok = 1 << 3
+ };
struct rtnl_handle rth;
struct {
- struct nlmsghdr n;
- struct rtmsg r;
- char buf[1024];
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
} req;
- char mxbuf[256];
+ char mxbuf[256];
struct rtattr * mxrta = (void*)mxbuf;
unsigned mxlock = 0;
- char *d = NULL;
- int gw_ok = 0;
- int dst_ok = 0;
- int proto_ok = 0;
- int type_ok = 0;
+ char *d = NULL;
+ smalluint ok = 0;
+ int arg;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_flags = NLM_F_REQUEST | flags;
req.n.nlmsg_type = cmd;
req.r.rtm_family = preferred_family;
- req.r.rtm_table = RT_TABLE_MAIN;
- req.r.rtm_scope = RT_SCOPE_NOWHERE;
+ if (RT_TABLE_MAIN) /* if it is zero, memset already did it */
+ req.r.rtm_table = RT_TABLE_MAIN;
+ if (RT_SCOPE_NOWHERE)
+ req.r.rtm_scope = RT_SCOPE_NOWHERE;
if (cmd != RTM_DELROUTE) {
req.r.rtm_protocol = RTPROT_BOOT;
@@ -318,61 +339,71 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
mxrta->rta_type = RTA_METRICS;
mxrta->rta_len = RTA_LENGTH(0);
- while (argc > 0) {
- if (strcmp(*argv, "src") == 0) {
+ while (*argv) {
+ arg = index_in_substrings(keywords, *argv);
+ if (arg == ARG_src) {
inet_prefix addr;
NEXT_ARG();
get_addr(&addr, *argv, req.r.rtm_family);
- if (req.r.rtm_family == AF_UNSPEC) {
+ if (req.r.rtm_family == AF_UNSPEC)
req.r.rtm_family = addr.family;
- }
addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
- } else if (strcmp(*argv, "via") == 0) {
+ } else if (arg == ARG_via) {
inet_prefix addr;
- gw_ok = 1;
+ ok |= gw_ok;
NEXT_ARG();
get_addr(&addr, *argv, req.r.rtm_family);
if (req.r.rtm_family == AF_UNSPEC) {
req.r.rtm_family = addr.family;
}
addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
- } else if (strcmp(*argv, "mtu") == 0) {
+ } else if (arg == ARG_mtu) {
unsigned mtu;
NEXT_ARG();
- if (strcmp(*argv, "lock") == 0) {
- mxlock |= (1<<RTAX_MTU);
+ if (index_in_strings(keywords, *argv) == PARM_lock) {
+ mxlock |= (1 << RTAX_MTU);
NEXT_ARG();
}
- if (get_unsigned(&mtu, *argv, 0)) {
- invarg("\"mtu\" value is invalid\n", *argv);
- }
+ mtu = get_unsigned(*argv, "mtu");
rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
- } else if (matches(*argv, "protocol") == 0) {
- int prot;
+ } else if (arg == ARG_protocol) {
+ uint32_t prot;
NEXT_ARG();
if (rtnl_rtprot_a2n(&prot, *argv))
- invarg("\"protocol\" value is invalid\n", *argv);
+ invarg(*argv, "protocol");
req.r.rtm_protocol = prot;
- proto_ok =1;
- } else if (strcmp(*argv, "dev") == 0 ||
- strcmp(*argv, "oif") == 0) {
+ ok |= proto_ok;
+#if ENABLE_FEATURE_IP_RULE
+ } else if (arg == ARG_table) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table");
+ req.r.rtm_table = tid;
+#endif
+ } else if (arg == ARG_dev || arg == ARG_oif) {
NEXT_ARG();
d = *argv;
+ } else if (arg == ARG_metric) {
+ uint32_t metric;
+ NEXT_ARG();
+ metric = get_u32(*argv, "metric");
+ addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
} else {
int type;
inet_prefix dst;
- if (strcmp(*argv, "to") == 0) {
+ if (arg == ARG_to) {
NEXT_ARG();
}
- if ((**argv < '0' || **argv > '9') &&
- rtnl_rtntype_a2n(&type, *argv) == 0) {
+ if ((**argv < '0' || **argv > '9')
+ && rtnl_rtntype_a2n(&type, *argv) == 0) {
NEXT_ARG();
req.r.rtm_type = type;
- type_ok = 1;
+ ok |= type_ok;
}
- if (dst_ok) {
+ if (ok & dst_ok) {
duparg2("to", *argv);
}
get_prefix(&dst, *argv, req.r.rtm_family);
@@ -380,17 +411,15 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
req.r.rtm_family = dst.family;
}
req.r.rtm_dst_len = dst.bitlen;
- dst_ok = 1;
+ ok |= dst_ok;
if (dst.bytelen) {
addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
}
}
- argc--; argv++;
+ argv++;
}
- if (rtnl_open(&rth, 0) < 0) {
- exit(1);
- }
+ xrtnl_open(&rth);
if (d) {
int idx;
@@ -398,10 +427,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
ll_init_map(&rth);
if (d) {
- if ((idx = ll_name_to_index(d)) == 0) {
- bb_error_msg("Cannot find device \"%s\"", d);
- return -1;
- }
+ idx = xll_name_to_index(d);
addattr32(&req.n, sizeof(req), RTA_OIF, idx);
}
}
@@ -413,12 +439,25 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
}
+ if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
+ req.r.rtm_scope = RT_SCOPE_HOST;
+ else if (req.r.rtm_type == RTN_BROADCAST ||
+ req.r.rtm_type == RTN_MULTICAST ||
+ req.r.rtm_type == RTN_ANYCAST)
+ req.r.rtm_scope = RT_SCOPE_LINK;
+ else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
+ if (cmd == RTM_DELROUTE)
+ req.r.rtm_scope = RT_SCOPE_NOWHERE;
+ else if (!(ok & gw_ok))
+ req.r.rtm_scope = RT_SCOPE_LINK;
+ }
+
if (req.r.rtm_family == AF_UNSPEC) {
req.r.rtm_family = AF_INET;
}
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
- exit(2);
+ return 2;
}
return 0;
@@ -437,37 +476,33 @@ static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
nladdr.nl_family = AF_NETLINK;
req.nlh.nlmsg_len = sizeof(req);
- req.nlh.nlmsg_type = RTM_GETROUTE;
- req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
- req.nlh.nlmsg_pid = 0;
+ if (RTM_GETROUTE)
+ req.nlh.nlmsg_type = RTM_GETROUTE;
+ if (NLM_F_ROOT | NLM_F_REQUEST)
+ req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+ /*req.nlh.nlmsg_pid = 0; - memset did it already */
req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
req.rtm.rtm_family = family;
- req.rtm.rtm_flags |= RTM_F_CLONED;
+ if (RTM_F_CLONED)
+ req.rtm.rtm_flags = RTM_F_CLONED;
- return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+ return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
}
-static int iproute_flush_cache(void)
+static void iproute_flush_cache(void)
{
-#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
-
- int len;
- int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
- char *buffer = "-1";
+ static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
+ int flush_fd = open_or_warn(fn, O_WRONLY);
if (flush_fd < 0) {
- fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
- return -1;
+ return;
}
- len = strlen (buffer);
-
- if ((write (flush_fd, (void *)buffer, len)) < len) {
- fprintf (stderr, "Cannot flush routing cache\n");
- return -1;
+ if (write(flush_fd, "-1", 2) < 2) {
+ bb_perror_msg("cannot flush routing cache");
+ return;
}
close(flush_fd);
- return 0;
}
static void iproute_reset_filter(void)
@@ -477,112 +512,150 @@ static void iproute_reset_filter(void)
filter.msrc.bitlen = -1;
}
-static int iproute_list_or_flush(int argc, char **argv, int flush)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_list_or_flush(char **argv, int flush)
{
int do_ipv6 = preferred_family;
struct rtnl_handle rth;
char *id = NULL;
char *od = NULL;
+ static const char keywords[] ALIGN1 =
+ /* "ip route list/flush" parameters: */
+ "protocol\0" "dev\0" "oif\0" "iif\0"
+ "via\0" "table\0" "cache\0"
+ "from\0" "to\0"
+ /* and possible further keywords */
+ "all\0"
+ "root\0"
+ "match\0"
+ "exact\0"
+ "main\0"
+ ;
+ enum {
+ KW_proto, KW_dev, KW_oif, KW_iif,
+ KW_via, KW_table, KW_cache,
+ KW_from, KW_to,
+ /* */
+ KW_all,
+ KW_root,
+ KW_match,
+ KW_exact,
+ KW_main,
+ };
+ int arg, parm;
iproute_reset_filter();
filter.tb = RT_TABLE_MAIN;
- if (flush && argc <= 0) {
- fprintf(stderr, "\"ip route flush\" requires arguments.\n");
- return -1;
- }
+ if (flush && !*argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
- while (argc > 0) {
- if (matches(*argv, "protocol") == 0) {
- int prot = 0;
+ while (*argv) {
+ arg = index_in_substrings(keywords, *argv);
+ if (arg == KW_proto) {
+ uint32_t prot = 0;
NEXT_ARG();
filter.protocolmask = -1;
if (rtnl_rtprot_a2n(&prot, *argv)) {
- if (strcmp(*argv, "all") != 0) {
- invarg("invalid \"protocol\"\n", *argv);
- }
+ if (index_in_strings(keywords, *argv) != KW_all)
+ invarg(*argv, "protocol");
prot = 0;
filter.protocolmask = 0;
}
filter.protocol = prot;
- } else if (strcmp(*argv, "dev") == 0 ||
- strcmp(*argv, "oif") == 0) {
+ } else if (arg == KW_dev || arg == KW_oif) {
NEXT_ARG();
od = *argv;
- } else if (strcmp(*argv, "iif") == 0) {
+ } else if (arg == KW_iif) {
NEXT_ARG();
id = *argv;
- } else if (matches(*argv, "from") == 0) {
+ } else if (arg == KW_via) {
+ NEXT_ARG();
+ get_prefix(&filter.rvia, *argv, do_ipv6);
+ } else if (arg == KW_table) { /* table all/cache/main */
+ NEXT_ARG();
+ parm = index_in_substrings(keywords, *argv);
+ if (parm == KW_cache)
+ filter.tb = -1;
+ else if (parm == KW_all)
+ filter.tb = 0;
+ else if (parm != KW_main) {
+#if ENABLE_FEATURE_IP_RULE
+ uint32_t tid;
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table");
+ filter.tb = tid;
+#else
+ invarg(*argv, "table");
+#endif
+ }
+ } else if (arg == KW_cache) {
+ /* The command 'ip route flush cache' is used by OpenSWAN.
+ * Assuming it's a synonym for 'ip route flush table cache' */
+ filter.tb = -1;
+ } else if (arg == KW_from) {
NEXT_ARG();
- if (matches(*argv, "root") == 0) {
+ parm = index_in_substrings(keywords, *argv);
+ if (parm == KW_root) {
NEXT_ARG();
get_prefix(&filter.rsrc, *argv, do_ipv6);
- } else if (matches(*argv, "match") == 0) {
+ } else if (parm == KW_match) {
NEXT_ARG();
get_prefix(&filter.msrc, *argv, do_ipv6);
} else {
- if (matches(*argv, "exact") == 0) {
+ if (parm == KW_exact)
NEXT_ARG();
- }
get_prefix(&filter.msrc, *argv, do_ipv6);
filter.rsrc = filter.msrc;
}
- } else {
- if (matches(*argv, "to") == 0) {
+ } else { /* "to" is the default parameter */
+ if (arg == KW_to) {
NEXT_ARG();
+ arg = index_in_substrings(keywords, *argv);
}
- if (matches(*argv, "root") == 0) {
+ /* parm = arg; - would be more plausible, but we reuse 'arg' here */
+ if (arg == KW_root) {
NEXT_ARG();
get_prefix(&filter.rdst, *argv, do_ipv6);
- } else if (matches(*argv, "match") == 0) {
+ } else if (arg == KW_match) {
NEXT_ARG();
get_prefix(&filter.mdst, *argv, do_ipv6);
- } else {
- if (matches(*argv, "exact") == 0) {
+ } else { /* "to exact" is the default */
+ if (arg == KW_exact)
NEXT_ARG();
- }
get_prefix(&filter.mdst, *argv, do_ipv6);
filter.rdst = filter.mdst;
}
}
- argc--; argv++;
+ argv++;
}
if (do_ipv6 == AF_UNSPEC && filter.tb) {
do_ipv6 = AF_INET;
}
- if (rtnl_open(&rth, 0) < 0) {
- exit(1);
- }
-
+ xrtnl_open(&rth);
ll_init_map(&rth);
if (id || od) {
int idx;
if (id) {
- if ((idx = ll_name_to_index(id)) == 0) {
- bb_error_msg("Cannot find device \"%s\"", id);
- return -1;
- }
+ idx = xll_name_to_index(id);
filter.iif = idx;
filter.iifmask = -1;
}
if (od) {
- if ((idx = ll_name_to_index(od)) == 0) {
- bb_error_msg("Cannot find device \"%s\"", od);
- }
+ idx = xll_name_to_index(od);
filter.oif = idx;
filter.oifmask = -1;
}
}
if (flush) {
- int round = 0;
char flushb[4096-512];
- if (filter.tb == -1) {
+ if (filter.tb == -1) { /* "flush table cache" */
if (do_ipv6 != AF_INET6)
iproute_flush_cache();
if (do_ipv6 == AF_INET)
@@ -595,79 +668,63 @@ static int iproute_list_or_flush(int argc, char **argv, int flush)
filter.rth = &rth;
for (;;) {
- if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
- perror("Cannot send dump request");
- return -1;
- }
+ xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
filter.flushed = 0;
- if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
- bb_error_msg("Flush terminated\n");
- return -1;
- }
- if (filter.flushed == 0) {
- if (round == 0) {
- if (filter.tb != -1 || do_ipv6 == AF_INET6)
- fprintf(stderr, "Nothing to flush.\n");
- }
- fflush(stdout);
+ xrtnl_dump_filter(&rth, print_route, NULL);
+ if (filter.flushed == 0)
return 0;
- }
- round++;
- if (flush_update() < 0)
- exit(1);
+ if (flush_update())
+ return 1;
}
}
if (filter.tb != -1) {
- if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
- bb_perror_msg_and_die("Cannot send dump request");
- }
- } else {
- if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
- bb_perror_msg_and_die("Cannot send dump request");
- }
- }
-
- if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
- bb_error_msg_and_die("Dump terminated");
+ xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
+ } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
}
+ xrtnl_dump_filter(&rth, print_route, NULL);
- exit(0);
+ return 0;
}
-static int iproute_get(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_get(char **argv)
{
struct rtnl_handle rth;
struct {
- struct nlmsghdr n;
- struct rtmsg r;
- char buf[1024];
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
} req;
- char *idev = NULL;
- char *odev = NULL;
- int connected = 0;
- int from_ok = 0;
- const char *options[] = { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
+ char *idev = NULL;
+ char *odev = NULL;
+ bool connected = 0;
+ bool from_ok = 0;
+ static const char options[] ALIGN1 =
+ "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
memset(&req, 0, sizeof(req));
iproute_reset_filter();
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
- req.n.nlmsg_type = RTM_GETROUTE;
+ if (NLM_F_REQUEST)
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ if (RTM_GETROUTE)
+ req.n.nlmsg_type = RTM_GETROUTE;
req.r.rtm_family = preferred_family;
- req.r.rtm_table = 0;
- req.r.rtm_protocol = 0;
- req.r.rtm_scope = 0;
- req.r.rtm_type = 0;
- req.r.rtm_src_len = 0;
- req.r.rtm_dst_len = 0;
- req.r.rtm_tos = 0;
-
- while (argc > 0) {
- switch (compare_string_array(options, *argv)) {
+ /*req.r.rtm_table = 0; - memset did this already */
+ /*req.r.rtm_protocol = 0;*/
+ /*req.r.rtm_scope = 0;*/
+ /*req.r.rtm_type = 0;*/
+ /*req.r.rtm_src_len = 0;*/
+ /*req.r.rtm_dst_len = 0;*/
+ /*req.r.rtm_tos = 0;*/
+
+ while (*argv) {
+ switch (index_in_strings(options, *argv)) {
case 0: /* from */
{
inet_prefix addr;
@@ -712,7 +769,7 @@ static int iproute_get(int argc, char **argv)
}
req.r.rtm_dst_len = addr.bitlen;
}
- argc--; argv++;
+ argv++;
}
}
@@ -720,8 +777,7 @@ static int iproute_get(int argc, char **argv)
bb_error_msg_and_die("need at least destination address");
}
- if (rtnl_open(&rth, 0) < 0)
- exit(1);
+ xrtnl_open(&rth);
ll_init_map(&rth);
@@ -729,17 +785,11 @@ static int iproute_get(int argc, char **argv)
int idx;
if (idev) {
- if ((idx = ll_name_to_index(idev)) == 0) {
- bb_error_msg("Cannot find device \"%s\"", idev);
- return -1;
- }
+ idx = xll_name_to_index(idev);
addattr32(&req.n, sizeof(req), RTA_IIF, idx);
}
if (odev) {
- if ((idx = ll_name_to_index(odev)) == 0) {
- bb_error_msg("Cannot find device \"%s\"", odev);
- return -1;
- }
+ idx = xll_name_to_index(odev);
addattr32(&req.n, sizeof(req), RTA_OIF, idx);
}
}
@@ -749,7 +799,7 @@ static int iproute_get(int argc, char **argv)
}
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
- exit(2);
+ return 2;
}
if (connected && !from_ok) {
@@ -757,18 +807,14 @@ static int iproute_get(int argc, char **argv)
int len = req.n.nlmsg_len;
struct rtattr * tb[RTA_MAX+1];
- if (print_route(NULL, &req.n, (void*)stdout) < 0) {
- bb_error_msg_and_die("An error :-)");
- }
+ print_route(NULL, &req.n, NULL);
if (req.n.nlmsg_type != RTM_NEWROUTE) {
- bb_error_msg("Not a route?");
- return -1;
+ bb_error_msg_and_die("not a route?");
}
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
- bb_error_msg("Wrong len %d", len);
- return -1;
+ bb_error_msg_and_die("wrong len %d", len);
}
memset(tb, 0, sizeof(tb));
@@ -778,8 +824,7 @@ static int iproute_get(int argc, char **argv)
tb[RTA_PREFSRC]->rta_type = RTA_SRC;
r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
} else if (!tb[RTA_SRC]) {
- bb_error_msg("Failed to connect the route");
- return -1;
+ bb_error_msg_and_die("failed to connect the route");
}
if (!odev && tb[RTA_OIF]) {
tb[RTA_OIF]->rta_type = 0;
@@ -794,30 +839,33 @@ static int iproute_get(int argc, char **argv)
req.n.nlmsg_type = RTM_GETROUTE;
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
- exit(2);
+ return 2;
}
}
-
- if (print_route(NULL, &req.n, (void*)stdout) < 0) {
- bb_error_msg_and_die("An error :-)");
- }
-
- exit(0);
+ print_route(NULL, &req.n, NULL);
+ return 0;
}
-int do_iproute(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iproute(char **argv)
{
- const char *ip_route_commands[] = { "add", "append", "change", "chg",
- "delete", "get", "list", "show", "prepend", "replace", "test", "flush", 0 };
- unsigned short command_num = 6;
- unsigned int flags = 0;
+ static const char ip_route_commands[] ALIGN1 =
+ /*0-3*/ "add\0""append\0""change\0""chg\0"
+ /*4-7*/ "delete\0""get\0""list\0""show\0"
+ /*8..*/ "prepend\0""replace\0""test\0""flush\0";
+ int command_num;
+ unsigned flags = 0;
int cmd = RTM_NEWROUTE;
- if (*argv) {
- command_num = compare_string_array(ip_route_commands, *argv);
- }
- switch(command_num) {
- case 0: /* add*/
+ if (!*argv)
+ return iproute_list_or_flush(argv, 0);
+
+ /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
+ /* It probably means that it is using "first match" rule */
+ command_num = index_in_substrings(ip_route_commands, *argv);
+
+ switch (command_num) {
+ case 0: /* add */
flags = NLM_F_CREATE|NLM_F_EXCL;
break;
case 1: /* append */
@@ -831,22 +879,24 @@ int do_iproute(int argc, char **argv)
cmd = RTM_DELROUTE;
break;
case 5: /* get */
- return iproute_get(argc-1, argv+1);
+ return iproute_get(argv+1);
case 6: /* list */
case 7: /* show */
- return iproute_list_or_flush(argc-1, argv+1, 0);
+ return iproute_list_or_flush(argv+1, 0);
case 8: /* prepend */
flags = NLM_F_CREATE;
+ break;
case 9: /* replace */
flags = NLM_F_CREATE|NLM_F_REPLACE;
+ break;
case 10: /* test */
flags = NLM_F_EXCL;
+ break;
case 11: /* flush */
- return iproute_list_or_flush(argc-1, argv+1, 1);
+ return iproute_list_or_flush(argv+1, 1);
default:
- bb_error_msg_and_die("Unknown command %s", *argv);
+ bb_error_msg_and_die("unknown command %s", *argv);
}
- return iproute_modify(cmd, flags, argc-1, argv+1);
+ return iproute_modify(cmd, flags, argv+1);
}
-
diff --git a/release/src/router/busybox/networking/libiproute/iprule.c b/release/src/router/busybox/networking/libiproute/iprule.c
new file mode 100644
index 00000000..6c90c6d2
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/iprule.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iprule.c "ip rule".
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * initially integrated into busybox by Bernhard Reutner-Fischer
+ */
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+/*
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
+ fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
+ fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n");
+ fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
+ fprintf(stderr, " [ prohibit | reject | unreachable ]\n");
+ fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
+ fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
+ exit(-1);
+}
+*/
+
+static int print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ int host_len = -1;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWRULE)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0)
+ return -1;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (r->rtm_family == AF_INET)
+ host_len = 32;
+ else if (r->rtm_family == AF_INET6)
+ host_len = 128;
+/* else if (r->rtm_family == AF_DECnet)
+ host_len = 16;
+ else if (r->rtm_family == AF_IPX)
+ host_len = 80;
+*/
+ if (tb[RTA_PRIORITY])
+ printf("%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY]));
+ else
+ printf("0:\t");
+
+ printf("from ");
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ printf("%s/%u", rt_addr_n2a(r->rtm_family,
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)),
+ r->rtm_src_len
+ );
+ } else {
+ fputs(format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)), stdout);
+ }
+ } else if (r->rtm_src_len) {
+ printf("0/%d", r->rtm_src_len);
+ } else {
+ printf("all");
+ }
+ bb_putchar(' ');
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)),
+ r->rtm_dst_len
+ );
+ } else {
+ printf("to %s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)));
+ }
+ } else if (r->rtm_dst_len) {
+ printf("to 0/%d ", r->rtm_dst_len);
+ }
+
+ if (r->rtm_tos) {
+ printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+ }
+ if (tb[RTA_PROTOINFO]) {
+ printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
+ }
+
+ if (tb[RTA_IIF]) {
+ printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
+ }
+
+ if (r->rtm_table)
+ printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1)));
+
+ if (tb[RTA_FLOW]) {
+ uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
+ uint32_t from = to>>16;
+ to &= 0xFFFF;
+ if (from) {
+ printf("realms %s/",
+ rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+ }
+ printf("%s ",
+ rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+ }
+
+ if (r->rtm_type == RTN_NAT) {
+ if (tb[RTA_GATEWAY]) {
+ printf("map-to %s ",
+ format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
+ } else
+ printf("masquerade");
+ } else if (r->rtm_type != RTN_UNICAST)
+ fputs(rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)), stdout);
+
+ bb_putchar('\n');
+ /*fflush(stdout);*/
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_list(char **argv)
+{
+ struct rtnl_handle rth;
+ int af = preferred_family;
+
+ if (af == AF_UNSPEC)
+ af = AF_INET;
+
+ if (*argv) {
+ //bb_error_msg("\"rule show\" needs no arguments");
+ bb_warn_ignoring_args(1);
+ return -1;
+ }
+
+ xrtnl_open(&rth);
+
+ xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
+ xrtnl_dump_filter(&rth, print_rule, NULL);
+
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_modify(int cmd, char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ "from\0""to\0""preference\0""order\0""priority\0"
+ "tos\0""fwmark\0""realms\0""table\0""lookup\0""dev\0"
+ "iif\0""nat\0""map-to\0""type\0""help\0";
+ enum {
+ ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
+ ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup, ARG_dev,
+ ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help
+ };
+ bool table_ok = 0;
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ smalluint key;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_type = cmd;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_protocol = RTPROT_BOOT;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.r.rtm_table = 0;
+ req.r.rtm_type = RTN_UNSPEC;
+
+ if (cmd == RTM_NEWRULE) {
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+ req.r.rtm_type = RTN_UNICAST;
+ }
+
+ while (*argv) {
+ key = index_in_substrings(keywords, *argv) + 1;
+ if (key == 0) /* no match found in keywords array, bail out. */
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ if (key == ARG_from) {
+ inet_prefix dst;
+ NEXT_ARG();
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ req.r.rtm_src_len = dst.bitlen;
+ addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
+ } else if (key == ARG_to) {
+ inet_prefix dst;
+ NEXT_ARG();
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ req.r.rtm_dst_len = dst.bitlen;
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+ } else if (key == ARG_preference ||
+ key == ARG_order ||
+ key == ARG_priority) {
+ uint32_t pref;
+ NEXT_ARG();
+ pref = get_u32(*argv, "preference");
+ addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
+ } else if (key == ARG_tos) {
+ uint32_t tos;
+ NEXT_ARG();
+ if (rtnl_dsfield_a2n(&tos, *argv))
+ invarg(*argv, "TOS");
+ req.r.rtm_tos = tos;
+ } else if (key == ARG_fwmark) {
+ uint32_t fwmark;
+ NEXT_ARG();
+ fwmark = get_u32(*argv, "fwmark");
+ addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
+ } else if (key == ARG_realms) {
+ uint32_t realm;
+ NEXT_ARG();
+ if (get_rt_realms(&realm, *argv))
+ invarg(*argv, "realms");
+ addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
+ } else if (key == ARG_table ||
+ key == ARG_lookup) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table ID");
+ req.r.rtm_table = tid;
+ table_ok = 1;
+ } else if (key == ARG_dev ||
+ key == ARG_iif) {
+ NEXT_ARG();
+ addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
+ } else if (key == ARG_nat ||
+ key == ARG_map_to) {
+ NEXT_ARG();
+ addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+ req.r.rtm_type = RTN_NAT;
+ } else {
+ int type;
+
+ if (key == ARG_type) {
+ NEXT_ARG();
+ }
+ if (key == ARG_help)
+ bb_show_usage();
+ if (rtnl_rtntype_a2n(&type, *argv))
+ invarg(*argv, "type");
+ req.r.rtm_type = type;
+ }
+ argv++;
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC)
+ req.r.rtm_family = AF_INET;
+
+ if (!table_ok && cmd == RTM_NEWRULE)
+ req.r.rtm_table = RT_TABLE_MAIN;
+
+ xrtnl_open(&rth);
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iprule(char **argv)
+{
+ static const char ip_rule_commands[] ALIGN1 =
+ "add\0""delete\0""list\0""show\0";
+ int cmd = 2; /* list */
+
+ if (!*argv)
+ return iprule_list(argv);
+
+ cmd = index_in_substrings(ip_rule_commands, *argv);
+ switch (cmd) {
+ case 0: /* add */
+ cmd = RTM_NEWRULE;
+ break;
+ case 1: /* delete */
+ cmd = RTM_DELRULE;
+ break;
+ case 2: /* list */
+ case 3: /* show */
+ return iprule_list(argv+1);
+ break;
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+ return iprule_modify(cmd, argv+1);
+}
diff --git a/release/src/router/busybox/networking/libiproute/iptunnel.c b/release/src/router/busybox/networking/libiproute/iptunnel.c
index eae5bb09..6a841aad 100644
--- a/release/src/router/busybox/networking/libiproute/iptunnel.c
+++ b/release/src/router/busybox/networking/libiproute/iptunnel.c
@@ -1,14 +1,11 @@
+/* vi: set sw=4 ts=4: */
/*
* iptunnel.c "ip tunnel"
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
- *
* Changes:
*
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
@@ -16,40 +13,71 @@
* Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
*/
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <arpa/inet.h>
#include <netinet/ip.h>
-#include <netinet/in.h>
-
#include <net/if.h>
#include <net/if_arp.h>
-
#include <asm/types.h>
+
+#ifndef __constant_htons
#define __constant_htons htons
-#include <linux/if_tunnel.h>
+#endif
+// FYI: #define SIOCDEVPRIVATE 0x89F0
+
+/* From linux/if_tunnel.h. #including it proved troublesome
+ * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
+#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
+#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
+#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
+#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
+//#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
+//#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
+//#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
+//#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
+#define GRE_CSUM __constant_htons(0x8000)
+//#define GRE_ROUTING __constant_htons(0x4000)
+#define GRE_KEY __constant_htons(0x2000)
+#define GRE_SEQ __constant_htons(0x1000)
+//#define GRE_STRICT __constant_htons(0x0800)
+//#define GRE_REC __constant_htons(0x0700)
+//#define GRE_FLAGS __constant_htons(0x00F8)
+//#define GRE_VERSION __constant_htons(0x0007)
+struct ip_tunnel_parm {
+ char name[IFNAMSIZ];
+ int link;
+ uint16_t i_flags;
+ uint16_t o_flags;
+ uint32_t i_key;
+ uint32_t o_key;
+ struct iphdr iph;
+};
+/* SIT-mode i_flags */
+//#define SIT_ISATAP 0x0001
+//struct ip_tunnel_prl {
+// uint32_t addr;
+// uint16_t flags;
+// uint16_t __reserved;
+// uint32_t datalen;
+// uint32_t __reserved2;
+// /* data follows */
+//};
+///* PRL flags */
+//#define PRL_DEFAULT 0x0001
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
#include "rt_names.h"
#include "utils.h"
-#include "libbb.h"
+/* Dies on error */
static int do_ioctl_get_ifindex(char *dev)
{
struct ifreq ifr;
int fd;
- strcpy(ifr.ifr_name, dev);
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
- bb_perror_msg("ioctl");
- return 0;
- }
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ xioctl(fd, SIOCGIFINDEX, &ifr);
close(fd);
return ifr.ifr_ifindex;
}
@@ -58,137 +86,145 @@ static int do_ioctl_get_iftype(char *dev)
{
struct ifreq ifr;
int fd;
+ int err;
- strcpy(ifr.ifr_name, dev);
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (ioctl(fd, SIOCGIFHWADDR, &ifr)) {
- bb_perror_msg("ioctl");
- return -1;
- }
+ strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr);
close(fd);
- return ifr.ifr_addr.sa_family;
+ return err ? -1 : ifr.ifr_addr.sa_family;
}
-
static char *do_ioctl_get_ifname(int idx)
{
- static struct ifreq ifr;
+ struct ifreq ifr;
int fd;
+ int err;
ifr.ifr_ifindex = idx;
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (ioctl(fd, SIOCGIFNAME, &ifr)) {
- bb_perror_msg("ioctl");
- return NULL;
- }
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr);
close(fd);
- return ifr.ifr_name;
+ return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name));
}
-
-
-static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p)
+static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
{
struct ifreq ifr;
int fd;
int err;
- strcpy(ifr.ifr_name, basedev);
+ strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
ifr.ifr_ifru.ifru_data = (void*)p;
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- err = ioctl(fd, SIOCGETTUNNEL, &ifr);
- if (err) {
- bb_perror_msg("ioctl");
- }
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr);
close(fd);
return err;
}
-static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p)
+/* Dies on error, otherwise returns 0 */
+static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
{
struct ifreq ifr;
int fd;
- int err;
if (cmd == SIOCCHGTUNNEL && p->name[0]) {
- strcpy(ifr.ifr_name, p->name);
+ strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
} else {
- strcpy(ifr.ifr_name, basedev);
+ strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
}
ifr.ifr_ifru.ifru_data = (void*)p;
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- err = ioctl(fd, cmd, &ifr);
- if (err) {
- bb_perror_msg("ioctl");
- }
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+#if ENABLE_IOCTL_HEX2STR_ERROR
+ /* #define magic will turn ioctl# into string */
+ if (cmd == SIOCCHGTUNNEL)
+ xioctl(fd, SIOCCHGTUNNEL, &ifr);
+ else
+ xioctl(fd, SIOCADDTUNNEL, &ifr);
+#else
+ xioctl(fd, cmd, &ifr);
+#endif
close(fd);
- return err;
+ return 0;
}
-static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p)
+/* Dies on error, otherwise returns 0 */
+static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
{
struct ifreq ifr;
int fd;
- int err;
if (p->name[0]) {
- strcpy(ifr.ifr_name, p->name);
+ strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
} else {
- strcpy(ifr.ifr_name, basedev);
+ strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
}
ifr.ifr_ifru.ifru_data = (void*)p;
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- err = ioctl(fd, SIOCDELTUNNEL, &ifr);
- if (err) {
- bb_perror_msg("ioctl");
- }
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ xioctl(fd, SIOCDELTUNNEL, &ifr);
close(fd);
- return err;
+ return 0;
}
-static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
+/* Dies on error */
+static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
{
+ static const char keywords[] ALIGN1 =
+ "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
+ "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
+ "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
+ "remote\0""any\0""local\0""dev\0"
+ "ttl\0""inherit\0""tos\0""dsfield\0"
+ "name\0";
+ enum {
+ ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip,
+ ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq,
+ ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc,
+ ARG_remote, ARG_any, ARG_local, ARG_dev,
+ ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield,
+ ARG_name
+ };
int count = 0;
char medium[IFNAMSIZ];
+ int key;
+
memset(p, 0, sizeof(*p));
- memset(&medium, 0, sizeof(medium));
+ medium[0] = '\0';
p->iph.version = 4;
p->iph.ihl = 5;
#ifndef IP_DF
-#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
+#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
#endif
p->iph.frag_off = htons(IP_DF);
- while (argc > 0) {
- if (strcmp(*argv, "mode") == 0) {
+ while (*argv) {
+ key = index_in_strings(keywords, *argv);
+ if (key == ARG_mode) {
NEXT_ARG();
- if (strcmp(*argv, "ipip") == 0 ||
- strcmp(*argv, "ip/ip") == 0) {
+ key = index_in_strings(keywords, *argv);
+ if (key == ARG_ipip ||
+ key == ARG_ip_ip) {
if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
- bb_error_msg("You managed to ask for more than one tunnel mode.");
- exit(-1);
+ bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
}
p->iph.protocol = IPPROTO_IPIP;
- } else if (strcmp(*argv, "gre") == 0 ||
- strcmp(*argv, "gre/ip") == 0) {
+ } else if (key == ARG_gre ||
+ key == ARG_gre_ip) {
if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
- bb_error_msg("You managed to ask for more than one tunnel mode.");
- exit(-1);
+ bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
}
p->iph.protocol = IPPROTO_GRE;
- } else if (strcmp(*argv, "sit") == 0 ||
- strcmp(*argv, "ipv6/ip") == 0) {
+ } else if (key == ARG_sit ||
+ key == ARG_ip6_ip) {
if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
- bb_error_msg("You managed to ask for more than one tunnel mode.");
- exit(-1);
+ bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
}
p->iph.protocol = IPPROTO_IPV6;
} else {
- bb_error_msg("Cannot guess tunnel mode.");
- exit(-1);
+ bb_error_msg_and_die("%s tunnel mode", "cannot guess");
}
- } else if (strcmp(*argv, "key") == 0) {
+ } else if (key == ARG_key) {
unsigned uval;
NEXT_ARG();
p->i_flags |= GRE_KEY;
@@ -196,107 +232,100 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
if (strchr(*argv, '.'))
p->i_key = p->o_key = get_addr32(*argv);
else {
- if (get_unsigned(&uval, *argv, 0)<0) {
- bb_error_msg("invalid value of \"key\"");
- exit(-1);
- }
+ uval = get_unsigned(*argv, "key");
p->i_key = p->o_key = htonl(uval);
}
- } else if (strcmp(*argv, "ikey") == 0) {
+ } else if (key == ARG_ikey) {
unsigned uval;
NEXT_ARG();
p->i_flags |= GRE_KEY;
if (strchr(*argv, '.'))
p->o_key = get_addr32(*argv);
else {
- if (get_unsigned(&uval, *argv, 0)<0) {
- bb_error_msg("invalid value of \"ikey\"");
- exit(-1);
- }
+ uval = get_unsigned(*argv, "ikey");
p->i_key = htonl(uval);
}
- } else if (strcmp(*argv, "okey") == 0) {
+ } else if (key == ARG_okey) {
unsigned uval;
NEXT_ARG();
p->o_flags |= GRE_KEY;
if (strchr(*argv, '.'))
p->o_key = get_addr32(*argv);
else {
- if (get_unsigned(&uval, *argv, 0)<0) {
- bb_error_msg("invalid value of \"okey\"");
- exit(-1);
- }
+ uval = get_unsigned(*argv, "okey");
p->o_key = htonl(uval);
}
- } else if (strcmp(*argv, "seq") == 0) {
+ } else if (key == ARG_seq) {
p->i_flags |= GRE_SEQ;
p->o_flags |= GRE_SEQ;
- } else if (strcmp(*argv, "iseq") == 0) {
+ } else if (key == ARG_iseq) {
p->i_flags |= GRE_SEQ;
- } else if (strcmp(*argv, "oseq") == 0) {
+ } else if (key == ARG_oseq) {
p->o_flags |= GRE_SEQ;
- } else if (strcmp(*argv, "csum") == 0) {
+ } else if (key == ARG_csum) {
p->i_flags |= GRE_CSUM;
p->o_flags |= GRE_CSUM;
- } else if (strcmp(*argv, "icsum") == 0) {
+ } else if (key == ARG_icsum) {
p->i_flags |= GRE_CSUM;
- } else if (strcmp(*argv, "ocsum") == 0) {
+ } else if (key == ARG_ocsum) {
p->o_flags |= GRE_CSUM;
- } else if (strcmp(*argv, "nopmtudisc") == 0) {
+ } else if (key == ARG_nopmtudisc) {
p->iph.frag_off = 0;
- } else if (strcmp(*argv, "pmtudisc") == 0) {
+ } else if (key == ARG_pmtudisc) {
p->iph.frag_off = htons(IP_DF);
- } else if (strcmp(*argv, "remote") == 0) {
+ } else if (key == ARG_remote) {
NEXT_ARG();
- if (strcmp(*argv, "any"))
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_any)
p->iph.daddr = get_addr32(*argv);
- } else if (strcmp(*argv, "local") == 0) {
+ } else if (key == ARG_local) {
NEXT_ARG();
- if (strcmp(*argv, "any"))
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_any)
p->iph.saddr = get_addr32(*argv);
- } else if (strcmp(*argv, "dev") == 0) {
+ } else if (key == ARG_dev) {
NEXT_ARG();
- strncpy(medium, *argv, IFNAMSIZ-1);
- } else if (strcmp(*argv, "ttl") == 0) {
+ strncpy_IFNAMSIZ(medium, *argv);
+ } else if (key == ARG_ttl) {
unsigned uval;
NEXT_ARG();
- if (strcmp(*argv, "inherit") != 0) {
- if (get_unsigned(&uval, *argv, 0))
- invarg("invalid TTL\n", *argv);
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_inherit) {
+ uval = get_unsigned(*argv, "TTL");
if (uval > 255)
- invarg("TTL must be <=255\n", *argv);
+ invarg(*argv, "TTL must be <=255");
p->iph.ttl = uval;
}
- } else if (strcmp(*argv, "tos") == 0 ||
- matches(*argv, "dsfield") == 0) {
- __u32 uval;
+ } else if (key == ARG_tos ||
+ key == ARG_dsfield) {
+ uint32_t uval;
NEXT_ARG();
- if (strcmp(*argv, "inherit") != 0) {
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_inherit) {
if (rtnl_dsfield_a2n(&uval, *argv))
- invarg("bad TOS value", *argv);
+ invarg(*argv, "TOS");
p->iph.tos = uval;
} else
p->iph.tos = 1;
} else {
- if (strcmp(*argv, "name") == 0) {
+ if (key == ARG_name) {
NEXT_ARG();
}
if (p->name[0])
duparg2("name", *argv);
- strncpy(p->name, *argv, IFNAMSIZ);
+ strncpy_IFNAMSIZ(p->name, *argv);
if (cmd == SIOCCHGTUNNEL && count == 0) {
struct ip_tunnel_parm old_p;
memset(&old_p, 0, sizeof(old_p));
if (do_get_ioctl(*argv, &old_p))
- return -1;
+ exit(EXIT_FAILURE);
*p = old_p;
}
}
count++;
- argc--; argv++;
+ argv++;
}
-
if (p->iph.protocol == 0) {
if (memcmp(p->name, "gre", 3) == 0)
p->iph.protocol = IPPROTO_GRE;
@@ -308,15 +337,12 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
- bb_error_msg("Keys are not allowed with ipip and sit.");
- return -1;
+ bb_error_msg_and_die("keys are not allowed with ipip and sit");
}
}
if (medium[0]) {
p->link = do_ioctl_get_ifindex(medium);
- if (p->link == 0)
- return -1;
}
if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
@@ -328,23 +354,19 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
p->o_flags |= GRE_KEY;
}
if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
- bb_error_msg("Broadcast tunnel requires a source address.");
- return -1;
+ bb_error_msg_and_die("broadcast tunnel requires a source address");
}
- return 0;
}
-
-static int do_add(int cmd, int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_add(int cmd, char **argv)
{
struct ip_tunnel_parm p;
- if (parse_args(argc, argv, cmd, &p) < 0)
- return -1;
+ parse_args(argv, cmd, &p);
if (p.iph.ttl && p.iph.frag_off == 0) {
- bb_error_msg("ttl != 0 and noptmudisc are incompatible");
- return -1;
+ bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
}
switch (p.iph.protocol) {
@@ -354,19 +376,17 @@ static int do_add(int cmd, int argc, char **argv)
return do_add_ioctl(cmd, "gre0", &p);
case IPPROTO_IPV6:
return do_add_ioctl(cmd, "sit0", &p);
- default:
- bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)");
- return -1;
+ default:
+ bb_error_msg_and_die("cannot determine tunnel mode (ipip, gre or sit)");
}
- return -1;
}
-int do_del(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_del(char **argv)
{
struct ip_tunnel_parm p;
- if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
- return -1;
+ parse_args(argv, SIOCDELTUNNEL, &p);
switch (p.iph.protocol) {
case IPPROTO_IPIP:
@@ -375,13 +395,12 @@ int do_del(int argc, char **argv)
return do_del_ioctl("gre0", &p);
case IPPROTO_IPV6:
return do_del_ioctl("sit0", &p);
- default:
+ default:
return do_del_ioctl(p.name, &p);
}
- return -1;
}
-void print_tunnel(struct ip_tunnel_parm *p)
+static void print_tunnel(struct ip_tunnel_parm *p)
{
char s1[256];
char s2[256];
@@ -401,8 +420,10 @@ void print_tunnel(struct ip_tunnel_parm *p)
p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
if (p->link) {
char *n = do_ioctl_get_ifname(p->link);
- if (n)
+ if (n) {
printf(" dev %s ", n);
+ free(n);
+ }
}
if (p->iph.ttl)
printf(" ttl %d ", p->iph.ttl);
@@ -411,63 +432,64 @@ void print_tunnel(struct ip_tunnel_parm *p)
if (p->iph.tos) {
SPRINT_BUF(b1);
printf(" tos");
- if (p->iph.tos&1)
+ if (p->iph.tos & 1)
printf(" inherit");
- if (p->iph.tos&~1)
- printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
- rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
+ if (p->iph.tos & ~1)
+ printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
+ rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
}
- if (!(p->iph.frag_off&htons(IP_DF)))
+ if (!(p->iph.frag_off & htons(IP_DF)))
printf(" nopmtudisc");
- if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
+ if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
printf(" key %s", s3);
- else if ((p->i_flags|p->o_flags)&GRE_KEY) {
- if (p->i_flags&GRE_KEY)
+ else if ((p->i_flags | p->o_flags) & GRE_KEY) {
+ if (p->i_flags & GRE_KEY)
printf(" ikey %s ", s3);
- if (p->o_flags&GRE_KEY)
+ if (p->o_flags & GRE_KEY)
printf(" okey %s ", s4);
}
- if (p->i_flags&GRE_SEQ)
- printf("%s Drop packets out of sequence.\n", _SL_);
- if (p->i_flags&GRE_CSUM)
- printf("%s Checksum in received packet is required.", _SL_);
- if (p->o_flags&GRE_SEQ)
- printf("%s Sequence packets on output.", _SL_);
- if (p->o_flags&GRE_CSUM)
- printf("%s Checksum output packets.", _SL_);
+ if (p->i_flags & GRE_SEQ)
+ printf("%c Drop packets out of sequence.\n", _SL_);
+ if (p->i_flags & GRE_CSUM)
+ printf("%c Checksum in received packet is required.", _SL_);
+ if (p->o_flags & GRE_SEQ)
+ printf("%c Sequence packets on output.", _SL_);
+ if (p->o_flags & GRE_CSUM)
+ printf("%c Checksum output packets.", _SL_);
}
-static int do_tunnels_list(struct ip_tunnel_parm *p)
+static void do_tunnels_list(struct ip_tunnel_parm *p)
{
char name[IFNAMSIZ];
- unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
- rx_fifo, rx_frame,
- tx_bytes, tx_packets, tx_errs, tx_drops,
- tx_fifo, tx_colls, tx_carrier, rx_multi;
+ unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
+ rx_fifo, rx_frame,
+ tx_bytes, tx_packets, tx_errs, tx_drops,
+ tx_fifo, tx_colls, tx_carrier, rx_multi;
int type;
struct ip_tunnel_parm p1;
-
char buf[512];
- FILE *fp = fopen("/proc/net/dev", "r");
+ FILE *fp = fopen_or_warn("/proc/net/dev", "r");
+
if (fp == NULL) {
- perror("fopen");
- return -1;
+ return;
}
-
+ /* skip headers */
fgets(buf, sizeof(buf), fp);
fgets(buf, sizeof(buf), fp);
while (fgets(buf, sizeof(buf), fp) != NULL) {
char *ptr;
- buf[sizeof(buf) - 1] = 0;
- if ((ptr = strchr(buf, ':')) == NULL ||
+
+ /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
+ ptr = strchr(buf, ':');
+ if (ptr == NULL ||
(*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
- bb_error_msg("Wrong format of /proc/net/dev. Sorry.");
- return -1;
+ bb_error_msg("wrong format of /proc/net/dev");
+ return;
}
- if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
+ if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
&rx_bytes, &rx_packets, &rx_errs, &rx_drops,
&rx_fifo, &rx_frame, &rx_multi,
&tx_bytes, &tx_packets, &tx_errs, &tx_drops,
@@ -477,7 +499,7 @@ static int do_tunnels_list(struct ip_tunnel_parm *p)
continue;
type = do_ioctl_get_iftype(name);
if (type == -1) {
- bb_error_msg("Failed to get type of [%s]", name);
+ bb_error_msg("cannot get type of [%s]", name);
continue;
}
if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
@@ -492,21 +514,20 @@ static int do_tunnels_list(struct ip_tunnel_parm *p)
(p->i_key && p1.i_key != p->i_key))
continue;
print_tunnel(&p1);
- printf("\n");
+ bb_putchar('\n');
}
- return 0;
}
-static int do_show(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_show(char **argv)
{
int err;
struct ip_tunnel_parm p;
- if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
- return -1;
+ parse_args(argv, SIOCGETTUNNEL, &p);
switch (p.iph.protocol) {
- case IPPROTO_IPIP:
+ case IPPROTO_IPIP:
err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
break;
case IPPROTO_GRE:
@@ -523,26 +544,29 @@ static int do_show(int argc, char **argv)
return -1;
print_tunnel(&p);
- printf("\n");
+ bb_putchar('\n');
return 0;
}
-int do_iptunnel(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iptunnel(char **argv)
{
- if (argc > 0) {
- if (matches(*argv, "add") == 0)
- return do_add(SIOCADDTUNNEL, argc-1, argv+1);
- if (matches(*argv, "change") == 0)
- return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
- if (matches(*argv, "del") == 0)
- return do_del(argc-1, argv+1);
- if (matches(*argv, "show") == 0 ||
- matches(*argv, "lst") == 0 ||
- matches(*argv, "list") == 0)
- return do_show(argc-1, argv+1);
- } else
- return do_show(0, NULL);
-
- bb_error_msg("Command \"%s\" is unknown, try \"ip tunnel help\".", *argv);
- exit(-1);
+ static const char keywords[] ALIGN1 =
+ "add\0""change\0""delete\0""show\0""list\0""lst\0";
+ enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst };
+ int key;
+
+ if (*argv) {
+ key = index_in_substrings(keywords, *argv);
+ if (key < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ argv++;
+ if (key == ARG_add)
+ return do_add(SIOCADDTUNNEL, argv);
+ if (key == ARG_change)
+ return do_add(SIOCCHGTUNNEL, argv);
+ if (key == ARG_del)
+ return do_del(argv);
+ }
+ return do_show(argv);
}
diff --git a/release/src/router/busybox/networking/libiproute/libnetlink.c b/release/src/router/busybox/networking/libiproute/libnetlink.c
index 9390e56f..6d51d8de 100644
--- a/release/src/router/busybox/networking/libiproute/libnetlink.c
+++ b/release/src/router/busybox/networking/libiproute/libnetlink.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* libnetlink.c RTnetlink service routines.
*
@@ -11,61 +12,41 @@
*/
#include <sys/socket.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
#include <sys/uio.h>
-#include "libnetlink.h"
#include "libbb.h"
+#include "libnetlink.h"
-void rtnl_close(struct rtnl_handle *rth)
+void FAST_FUNC rtnl_close(struct rtnl_handle *rth)
{
close(rth->fd);
}
-int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+int FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
{
- int addr_len;
+ socklen_t addr_len;
memset(rth, 0, sizeof(rth));
- rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (rth->fd < 0) {
- bb_perror_msg("Cannot open netlink socket");
- return -1;
- }
+ rth->fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
- rth->local.nl_groups = subscriptions;
+ /*rth->local.nl_groups = subscriptions;*/
- if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
- bb_perror_msg("Cannot bind netlink socket");
- return -1;
- }
+ xbind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local));
addr_len = sizeof(rth->local);
- if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
- bb_perror_msg("Cannot getsockname");
- return -1;
- }
- if (addr_len != sizeof(rth->local)) {
- bb_error_msg("Wrong address length %d", addr_len);
- return -1;
- }
- if (rth->local.nl_family != AF_NETLINK) {
- bb_error_msg("Wrong address family %d", rth->local.nl_family);
- return -1;
- }
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0)
+ bb_perror_msg_and_die("getsockname");
+ if (addr_len != sizeof(rth->local))
+ bb_error_msg_and_die("wrong address length %d", addr_len);
+ if (rth->local.nl_family != AF_NETLINK)
+ bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
rth->seq = time(NULL);
return 0;
}
-int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
{
struct {
struct nlmsghdr nlh;
@@ -83,20 +64,21 @@ int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
req.g.rtgen_family = family;
- return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+ return xsendto(rth->fd, (void*)&req, sizeof(req),
+ (struct sockaddr*)&nladdr, sizeof(nladdr));
}
-int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
+int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
{
struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
- return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+ return xsendto(rth->fd, buf, len, (struct sockaddr*)&nladdr, sizeof(nladdr));
}
-int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
{
struct nlmsghdr nlh;
struct sockaddr_nl nladdr;
@@ -120,15 +102,16 @@ int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
return sendmsg(rth->fd, &msg, 0);
}
-int rtnl_dump_filter(struct rtnl_handle *rth,
- int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
- void *arg1,
- int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *arg2)
+static int rtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg1/*,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg2*/)
{
- char buf[8192];
+ int retval = -1;
+ char *buf = xmalloc(8*1024); /* avoid big stack buffer */
struct sockaddr_nl nladdr;
- struct iovec iov = { buf, sizeof(buf) };
+ struct iovec iov = { buf, 8*1024 };
while (1) {
int status;
@@ -151,7 +134,7 @@ int rtnl_dump_filter(struct rtnl_handle *rth,
}
if (status == 0) {
bb_error_msg("EOF on netlink");
- return -1;
+ goto ret;
}
if (msg.msg_namelen != sizeof(nladdr)) {
bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
@@ -161,19 +144,21 @@ int rtnl_dump_filter(struct rtnl_handle *rth,
while (NLMSG_OK(h, status)) {
int err;
- if (h->nlmsg_pid != rth->local.nl_pid ||
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
h->nlmsg_seq != rth->dump) {
- if (junk) {
- err = junk(&nladdr, h, arg2);
- if (err < 0) {
- return err;
- }
- }
+// if (junk) {
+// err = junk(&nladdr, h, arg2);
+// if (err < 0) {
+// retval = err;
+// goto ret;
+// }
+// }
goto skip_it;
}
if (h->nlmsg_type == NLMSG_DONE) {
- return 0;
+ goto ret_0;
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
@@ -183,37 +168,60 @@ int rtnl_dump_filter(struct rtnl_handle *rth,
errno = -l_err->error;
bb_perror_msg("RTNETLINK answers");
}
- return -1;
+ goto ret;
}
err = filter(&nladdr, h, arg1);
if (err < 0) {
- return err;
+ retval = err;
+ goto ret;
}
-skip_it:
+ skip_it:
h = NLMSG_NEXT(h, status);
}
if (msg.msg_flags & MSG_TRUNC) {
- bb_error_msg("Message truncated");
+ bb_error_msg("message truncated");
continue;
}
if (status) {
- bb_error_msg_and_die("!!!Remnant of size %d", status);
+ bb_error_msg_and_die("remnant of size %d!", status);
}
- }
+ } /* while (1) */
+ ret_0:
+ retval++; /* = 0 */
+ ret:
+ free(buf);
+ return retval;
}
-int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
- unsigned groups, struct nlmsghdr *answer,
- int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+int FAST_FUNC xrtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *, void *),
+ void *arg1)
+{
+ int ret = rtnl_dump_filter(rth, filter, arg1/*, NULL, NULL*/);
+ if (ret < 0)
+ bb_error_msg_and_die("dump terminated");
+ return ret;
+}
+
+int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+ pid_t peer, unsigned groups,
+ struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *, void *),
void *jarg)
{
+/* bbox doesn't use parameters no. 3, 4, 6, 7, they are stubbed out */
+#define peer 0
+#define groups 0
+#define junk NULL
+#define jarg NULL
+ int retval = -1;
int status;
unsigned seq;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov = { (void*)n, n->nlmsg_len };
- char buf[8192];
+ char *buf = xmalloc(8*1024); /* avoid big stack buffer */
struct msghdr msg = {
(void*)&nladdr, sizeof(nladdr),
&iov, 1,
@@ -223,8 +231,8 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = peer;
- nladdr.nl_groups = groups;
+// nladdr.nl_pid = peer;
+// nladdr.nl_groups = groups;
n->nlmsg_seq = seq = ++rtnl->seq;
if (answer == NULL) {
@@ -233,14 +241,14 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0) {
- bb_perror_msg("Cannot talk to rtnetlink");
- return -1;
+ bb_perror_msg("cannot talk to rtnetlink");
+ goto ret;
}
iov.iov_base = buf;
while (1) {
- iov.iov_len = sizeof(buf);
+ iov.iov_len = 8*1024;
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0) {
@@ -252,219 +260,98 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
}
if (status == 0) {
bb_error_msg("EOF on netlink");
- return -1;
+ goto ret;
}
if (msg.msg_namelen != sizeof(nladdr)) {
bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
}
- for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
- int l_err;
+ for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+// int l_err;
int len = h->nlmsg_len;
int l = len - sizeof(*h);
- if (l<0 || len>status) {
+ if (l < 0 || len > status) {
if (msg.msg_flags & MSG_TRUNC) {
- bb_error_msg("Truncated message");
- return -1;
+ bb_error_msg("truncated message");
+ goto ret;
}
- bb_error_msg_and_die("!!!malformed message: len=%d", len);
+ bb_error_msg_and_die("malformed message: len=%d!", len);
}
- if (h->nlmsg_pid != rtnl->local.nl_pid ||
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
h->nlmsg_seq != seq) {
- if (junk) {
- l_err = junk(&nladdr, h, jarg);
- if (l_err < 0) {
- return l_err;
- }
- }
+// if (junk) {
+// l_err = junk(&nladdr, h, jarg);
+// if (l_err < 0) {
+// retval = l_err;
+// goto ret;
+// }
+// }
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
- if (l < sizeof(struct nlmsgerr)) {
+ if (l < (int)sizeof(struct nlmsgerr)) {
bb_error_msg("ERROR truncated");
} else {
- errno = -err->error;
+ errno = - err->error;
if (errno == 0) {
if (answer) {
memcpy(answer, h, h->nlmsg_len);
}
- return 0;
+ goto ret_0;
}
bb_perror_msg("RTNETLINK answers");
}
- return -1;
+ goto ret;
}
if (answer) {
memcpy(answer, h, h->nlmsg_len);
- return 0;
+ goto ret_0;
}
- bb_error_msg("Unexpected reply!!!");
+ bb_error_msg("unexpected reply!");
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
- bb_error_msg("Message truncated");
+ bb_error_msg("message truncated");
continue;
}
if (status) {
- bb_error_msg_and_die("!!!Remnant of size %d", status);
- }
- }
-}
-
-int rtnl_listen(struct rtnl_handle *rtnl,
- int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *jarg)
-{
- int status;
- struct nlmsghdr *h;
- struct sockaddr_nl nladdr;
- struct iovec iov;
- char buf[8192];
- struct msghdr msg = {
- (void*)&nladdr, sizeof(nladdr),
- &iov, 1,
- NULL, 0,
- 0
- };
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = 0;
- nladdr.nl_groups = 0;
-
-
- iov.iov_base = buf;
-
- while (1) {
- iov.iov_len = sizeof(buf);
- status = recvmsg(rtnl->fd, &msg, 0);
-
- if (status < 0) {
- if (errno == EINTR)
- continue;
- bb_perror_msg("OVERRUN");
- continue;
+ bb_error_msg_and_die("remnant of size %d!", status);
}
- if (status == 0) {
- bb_error_msg("EOF on netlink");
- return -1;
- }
- if (msg.msg_namelen != sizeof(nladdr)) {
- bb_error_msg_and_die("Sender address length == %d", msg.msg_namelen);
- }
- for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
- int err;
- int len = h->nlmsg_len;
- int l = len - sizeof(*h);
-
- if (l<0 || len>status) {
- if (msg.msg_flags & MSG_TRUNC) {
- bb_error_msg("Truncated message");
- return -1;
- }
- bb_error_msg_and_die("!!!malformed message: len=%d", len);
- }
-
- err = handler(&nladdr, h, jarg);
- if (err < 0) {
- return err;
- }
-
- status -= NLMSG_ALIGN(len);
- h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
- }
- if (msg.msg_flags & MSG_TRUNC) {
- bb_error_msg("Message truncated");
- continue;
- }
- if (status) {
- bb_error_msg_and_die("!!!Remnant of size %d", status);
- }
- }
-}
-
-int rtnl_from_file(FILE *rtnl,
- int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *jarg)
-{
- int status;
- struct sockaddr_nl nladdr;
- char buf[8192];
- struct nlmsghdr *h = (void*)buf;
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = 0;
- nladdr.nl_groups = 0;
-
- while (1) {
- int err, len, type;
- int l;
-
- status = fread(&buf, 1, sizeof(*h), rtnl);
-
- if (status < 0) {
- if (errno == EINTR)
- continue;
- bb_perror_msg("rtnl_from_file: fread");
- return -1;
- }
- if (status == 0)
- return 0;
-
- len = h->nlmsg_len;
- type= h->nlmsg_type;
- l = len - sizeof(*h);
-
- if (l<0 || len>sizeof(buf)) {
- bb_error_msg("!!!malformed message: len=%d @%lu",
- len, ftell(rtnl));
- return -1;
- }
-
- status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
-
- if (status < 0) {
- bb_perror_msg("rtnl_from_file: fread");
- return -1;
- }
- if (status < l) {
- bb_error_msg("rtnl-from_file: truncated message");
- return -1;
- }
-
- err = handler(&nladdr, h, jarg);
- if (err < 0)
- return err;
- }
+ } /* while (1) */
+ ret_0:
+ retval++; /* = 0 */
+ ret:
+ free(buf);
+ return retval;
}
-int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
{
int len = RTA_LENGTH(4);
struct rtattr *rta;
- if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
return -1;
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
- memcpy(RTA_DATA(rta), &data, 4);
+ move_to_unaligned32(RTA_DATA(rta), data);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
-int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
- if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
return -1;
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
@@ -474,7 +361,7 @@ int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
return 0;
}
-int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data)
{
int len = RTA_LENGTH(4);
struct rtattr *subrta;
@@ -485,12 +372,12 @@ int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
- memcpy(RTA_DATA(subrta), &data, 4);
+ move_to_unaligned32(RTA_DATA(subrta), data);
rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
return 0;
}
-int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
{
struct rtattr *subrta;
int len = RTA_LENGTH(alen);
@@ -507,7 +394,7 @@ int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen
}
-int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+int FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max) {
@@ -516,7 +403,7 @@ int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
rta = RTA_NEXT(rta,len);
}
if (len) {
- bb_error_msg("!!!Deficit %d, rta_len=%d", len, rta->rta_len);
+ bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
}
return 0;
}
diff --git a/release/src/router/busybox/networking/libiproute/libnetlink.h b/release/src/router/busybox/networking/libiproute/libnetlink.h
index 45d3ad2b..e5fee4dd 100644
--- a/release/src/router/busybox/networking/libiproute/libnetlink.h
+++ b/release/src/router/busybox/networking/libiproute/libnetlink.h
@@ -1,46 +1,50 @@
-#ifndef __LIBNETLINK_H__
-#define __LIBNETLINK_H__ 1
+/* vi: set sw=4 ts=4: */
+#ifndef LIBNETLINK_H
+#define LIBNETLINK_H 1
-#include <asm/types.h>
+#include <linux/types.h>
+/* We need linux/types.h because older kernels use __u32 etc
+ * in linux/[rt]netlink.h. 2.6.19 seems to be ok, though */
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
struct rtnl_handle
{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
- __u32 seq;
- __u32 dump;
+ uint32_t seq;
+ uint32_t dump;
};
-extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
-extern void rtnl_close(struct rtnl_handle *rth);
-extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
-extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
-extern int rtnl_dump_filter(struct rtnl_handle *rth,
- int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
- void *arg1,
- int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *arg2);
+extern int xrtnl_open(struct rtnl_handle *rth) FAST_FUNC;
+extern void rtnl_close(struct rtnl_handle *rth) FAST_FUNC;
+extern int xrtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type) FAST_FUNC;
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) FAST_FUNC;
+extern int xrtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(const struct sockaddr_nl*, struct nlmsghdr *n, void*),
+ void *arg1) FAST_FUNC;
+
+/* bbox doesn't use parameters no. 3, 4, 6, 7, stub them out */
+#define rtnl_talk(rtnl, n, peer, groups, answer, junk, jarg) \
+ rtnl_talk(rtnl, n, answer)
extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
- unsigned groups, struct nlmsghdr *answer,
- int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *jarg);
-extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg) FAST_FUNC;
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int) FAST_FUNC;
-extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
-extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
-extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
-extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
-extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) FAST_FUNC;
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) FAST_FUNC;
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data) FAST_FUNC;
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) FAST_FUNC;
-extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *jarg);
-extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *jarg);
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) FAST_FUNC;
-#endif /* __LIBNETLINK_H__ */
+POP_SAVED_FUNCTION_VISIBILITY
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/linux/pkt_sched.h b/release/src/router/busybox/networking/libiproute/linux/pkt_sched.h
deleted file mode 100644
index e174588f..00000000
--- a/release/src/router/busybox/networking/libiproute/linux/pkt_sched.h
+++ /dev/null
@@ -1,413 +0,0 @@
-#ifndef __LINUX_PKT_SCHED_H
-#define __LINUX_PKT_SCHED_H
-
-/* Logical priority bands not depending on specific packet scheduler.
- Every scheduler will map them to real traffic classes, if it has
- no more precise mechanism to classify packets.
-
- These numbers have no special meaning, though their coincidence
- with obsolete IPv6 values is not occasional :-). New IPv6 drafts
- preferred full anarchy inspired by diffserv group.
-
- Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
- class, actually, as rule it will be handled with more care than
- filler or even bulk.
- */
-
-#include <asm/types.h>
-
-#define TC_PRIO_BESTEFFORT 0
-#define TC_PRIO_FILLER 1
-#define TC_PRIO_BULK 2
-#define TC_PRIO_INTERACTIVE_BULK 4
-#define TC_PRIO_INTERACTIVE 6
-#define TC_PRIO_CONTROL 7
-
-#define TC_PRIO_MAX 15
-
-/* Generic queue statistics, available for all the elements.
- Particular schedulers may have also their private records.
- */
-
-struct tc_stats
-{
- __u64 bytes; /* NUmber of enqueues bytes */
- __u32 packets; /* Number of enqueued packets */
- __u32 drops; /* Packets dropped because of lack of resources */
- __u32 overlimits; /* Number of throttle events when this
- * flow goes out of allocated bandwidth */
- __u32 bps; /* Current flow byte rate */
- __u32 pps; /* Current flow packet rate */
- __u32 qlen;
- __u32 backlog;
-#ifdef __KERNEL__
- spinlock_t *lock;
-#endif
-};
-
-struct tc_estimator
-{
- char interval;
- unsigned char ewma_log;
-};
-
-/* "Handles"
- ---------
-
- All the traffic control objects have 32bit identifiers, or "handles".
-
- They can be considered as opaque numbers from user API viewpoint,
- but actually they always consist of two fields: major and
- minor numbers, which are interpreted by kernel specially,
- that may be used by applications, though not recommended.
-
- F.e. qdisc handles always have minor number equal to zero,
- classes (or flows) have major equal to parent qdisc major, and
- minor uniquely identifying class inside qdisc.
-
- Macros to manipulate handles:
- */
-
-#define TC_H_MAJ_MASK (0xFFFF0000U)
-#define TC_H_MIN_MASK (0x0000FFFFU)
-#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
-#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
-#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
-
-#define TC_H_UNSPEC (0U)
-#define TC_H_ROOT (0xFFFFFFFFU)
-#define TC_H_INGRESS (0xFFFFFFF1U)
-
-struct tc_ratespec
-{
- unsigned char cell_log;
- unsigned char __reserved;
- unsigned short feature;
- short addend;
- unsigned short mpu;
- __u32 rate;
-};
-
-/* FIFO section */
-
-struct tc_fifo_qopt
-{
- __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
-};
-
-/* PRIO section */
-
-#define TCQ_PRIO_BANDS 16
-
-struct tc_prio_qopt
-{
- int bands; /* Number of bands */
- __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
-};
-
-/* CSZ section */
-
-struct tc_csz_qopt
-{
- int flows; /* Maximal number of guaranteed flows */
- unsigned char R_log; /* Fixed point position for round number */
- unsigned char delta_log; /* Log of maximal managed time interval */
- __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
-};
-
-struct tc_csz_copt
-{
- struct tc_ratespec slice;
- struct tc_ratespec rate;
- struct tc_ratespec peakrate;
- __u32 limit;
- __u32 buffer;
- __u32 mtu;
-};
-
-enum
-{
- TCA_CSZ_UNSPEC,
- TCA_CSZ_PARMS,
- TCA_CSZ_RTAB,
- TCA_CSZ_PTAB,
-};
-
-/* TBF section */
-
-struct tc_tbf_qopt
-{
- struct tc_ratespec rate;
- struct tc_ratespec peakrate;
- __u32 limit;
- __u32 buffer;
- __u32 mtu;
-};
-
-enum
-{
- TCA_TBF_UNSPEC,
- TCA_TBF_PARMS,
- TCA_TBF_RTAB,
- TCA_TBF_PTAB,
-};
-
-
-/* TEQL section */
-
-/* TEQL does not require any parameters */
-
-/* SFQ section */
-
-struct tc_sfq_qopt
-{
- unsigned quantum; /* Bytes per round allocated to flow */
- int perturb_period; /* Period of hash perturbation */
- __u32 limit; /* Maximal packets in queue */
- unsigned divisor; /* Hash divisor */
- unsigned flows; /* Maximal number of flows */
-};
-
-/*
- * NOTE: limit, divisor and flows are hardwired to code at the moment.
- *
- * limit=flows=128, divisor=1024;
- *
- * The only reason for this is efficiency, it is possible
- * to change these parameters in compile time.
- */
-
-/* RED section */
-
-enum
-{
- TCA_RED_UNSPEC,
- TCA_RED_PARMS,
- TCA_RED_STAB,
-};
-
-struct tc_red_qopt
-{
- __u32 limit; /* HARD maximal queue length (bytes) */
- __u32 qth_min; /* Min average length threshold (bytes) */
- __u32 qth_max; /* Max average length threshold (bytes) */
- unsigned char Wlog; /* log(W) */
- unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
- unsigned char Scell_log; /* cell size for idle damping */
- unsigned char flags;
-#define TC_RED_ECN 1
-};
-
-struct tc_red_xstats
-{
- __u32 early; /* Early drops */
- __u32 pdrop; /* Drops due to queue limits */
- __u32 other; /* Drops due to drop() calls */
- __u32 marked; /* Marked packets */
-};
-
-/* GRED section */
-
-#define MAX_DPs 16
-
-enum
-{
- TCA_GRED_UNSPEC,
- TCA_GRED_PARMS,
- TCA_GRED_STAB,
- TCA_GRED_DPS,
-};
-
-#define TCA_SET_OFF TCA_GRED_PARMS
-struct tc_gred_qopt
-{
- __u32 limit; /* HARD maximal queue length (bytes)
-*/
- __u32 qth_min; /* Min average length threshold (bytes)
-*/
- __u32 qth_max; /* Max average length threshold (bytes)
-*/
- __u32 DP; /* upto 2^32 DPs */
- __u32 backlog;
- __u32 qave;
- __u32 forced;
- __u32 early;
- __u32 other;
- __u32 pdrop;
-
- unsigned char Wlog; /* log(W) */
- unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
- unsigned char Scell_log; /* cell size for idle damping */
- __u8 prio; /* prio of this VQ */
- __u32 packets;
- __u32 bytesin;
-};
-/* gred setup */
-struct tc_gred_sopt
-{
- __u32 DPs;
- __u32 def_DP;
- __u8 grio;
-};
-
-/* HTB section */
-#define TC_HTB_NUMPRIO 4
-#define TC_HTB_MAXDEPTH 4
-
-struct tc_htb_opt
-{
- struct tc_ratespec rate;
- struct tc_ratespec ceil;
- __u32 buffer;
- __u32 cbuffer;
- __u32 quantum; /* out only */
- __u32 level; /* out only */
- __u8 prio;
- __u8 injectd; /* inject class distance */
- __u8 pad[2];
-};
-struct tc_htb_glob
-{
- __u32 rate2quantum; /* bps->quantum divisor */
- __u32 defcls; /* default class number */
- __u32 use_dcache; /* use dequeue cache ? */
- __u32 debug; /* debug flags */
-
-
- /* stats */
- __u32 deq_rate; /* dequeue rate */
- __u32 utilz; /* dequeue utilization */
- __u32 trials; /* deq_prio trials per dequeue */
- __u32 dcache_hits;
- __u32 direct_pkts; /* count of non shapped packets */
-};
-enum
-{
- TCA_HTB_UNSPEC,
- TCA_HTB_PARMS,
- TCA_HTB_INIT,
- TCA_HTB_CTAB,
- TCA_HTB_RTAB,
-};
-struct tc_htb_xstats
-{
- __u32 lends;
- __u32 borrows;
- __u32 giants; /* too big packets (rate will not be accurate) */
- __u32 injects; /* how many times leaf used injected bw */
- __u32 tokens;
- __u32 ctokens;
-};
-
-/* CBQ section */
-
-#define TC_CBQ_MAXPRIO 8
-#define TC_CBQ_MAXLEVEL 8
-#define TC_CBQ_DEF_EWMA 5
-
-struct tc_cbq_lssopt
-{
- unsigned char change;
- unsigned char flags;
-#define TCF_CBQ_LSS_BOUNDED 1
-#define TCF_CBQ_LSS_ISOLATED 2
- unsigned char ewma_log;
- unsigned char level;
-#define TCF_CBQ_LSS_FLAGS 1
-#define TCF_CBQ_LSS_EWMA 2
-#define TCF_CBQ_LSS_MAXIDLE 4
-#define TCF_CBQ_LSS_MINIDLE 8
-#define TCF_CBQ_LSS_OFFTIME 0x10
-#define TCF_CBQ_LSS_AVPKT 0x20
- __u32 maxidle;
- __u32 minidle;
- __u32 offtime;
- __u32 avpkt;
-};
-
-struct tc_cbq_wrropt
-{
- unsigned char flags;
- unsigned char priority;
- unsigned char cpriority;
- unsigned char __reserved;
- __u32 allot;
- __u32 weight;
-};
-
-struct tc_cbq_ovl
-{
- unsigned char strategy;
-#define TC_CBQ_OVL_CLASSIC 0
-#define TC_CBQ_OVL_DELAY 1
-#define TC_CBQ_OVL_LOWPRIO 2
-#define TC_CBQ_OVL_DROP 3
-#define TC_CBQ_OVL_RCLASSIC 4
- unsigned char priority2;
- __u32 penalty;
-};
-
-struct tc_cbq_police
-{
- unsigned char police;
- unsigned char __res1;
- unsigned short __res2;
-};
-
-struct tc_cbq_fopt
-{
- __u32 split;
- __u32 defmap;
- __u32 defchange;
-};
-
-struct tc_cbq_xstats
-{
- __u32 borrows;
- __u32 overactions;
- __s32 avgidle;
- __s32 undertime;
-};
-
-enum
-{
- TCA_CBQ_UNSPEC,
- TCA_CBQ_LSSOPT,
- TCA_CBQ_WRROPT,
- TCA_CBQ_FOPT,
- TCA_CBQ_OVL_STRATEGY,
- TCA_CBQ_RATE,
- TCA_CBQ_RTAB,
- TCA_CBQ_POLICE,
-};
-
-#define TCA_CBQ_MAX TCA_CBQ_POLICE
-
-/* dsmark section */
-
-enum {
- TCA_DSMARK_UNSPEC,
- TCA_DSMARK_INDICES,
- TCA_DSMARK_DEFAULT_INDEX,
- TCA_DSMARK_SET_TC_INDEX,
- TCA_DSMARK_MASK,
- TCA_DSMARK_VALUE
-};
-
-#define TCA_DSMARK_MAX TCA_DSMARK_VALUE
-
-/* ATM section */
-
-enum {
- TCA_ATM_UNSPEC,
- TCA_ATM_FD, /* file/socket descriptor */
- TCA_ATM_PTR, /* pointer to descriptor - later */
- TCA_ATM_HDR, /* LL header */
- TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */
- TCA_ATM_ADDR, /* PVC address (for output only) */
- TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */
-};
-
-#define TCA_ATM_MAX TCA_ATM_STATE
-
-#endif
diff --git a/release/src/router/busybox/networking/libiproute/ll_addr.c b/release/src/router/busybox/networking/libiproute/ll_addr.c
index ada685f4..f50e3719 100644
--- a/release/src/router/busybox/networking/libiproute/ll_addr.c
+++ b/release/src/router/busybox/networking/libiproute/ll_addr.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* ll_addr.c
*
@@ -9,11 +10,12 @@
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*/
-#include <arpa/inet.h>
-#include <string.h>
#include <net/if_arp.h>
-#include "utils.h"
+
#include "libbb.h"
+#include "rt_names.h"
+#include "utils.h"
+
const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
{
@@ -27,7 +29,7 @@ const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int
l = 0;
for (i=0; i<alen; i++) {
if (i==0) {
- snprintf(buf+l, blen, "%02x", addr[i]);
+ snprintf(buf+l, blen, ":%02x"+1, addr[i]);
blen -= 2;
l += 2;
} else {
@@ -41,10 +43,12 @@ const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int
int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
{
+ int i;
+
if (strchr(arg, '.')) {
inet_prefix pfx;
if (get_addr_1(&pfx, arg, AF_INET)) {
- bb_error_msg("\"%s\" is invalid lladdr.", arg);
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
return -1;
}
if (len < 4) {
@@ -52,30 +56,24 @@ int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
}
memcpy(lladdr, pfx.data, 4);
return 4;
- } else {
- int i;
+ }
- for (i=0; i<len; i++) {
- int temp;
- char *cp = strchr(arg, ':');
- if (cp) {
- *cp = 0;
- cp++;
- }
- if (sscanf(arg, "%x", &temp) != 1) {
- bb_error_msg("\"%s\" is invalid lladdr.", arg);
- return -1;
- }
- if (temp < 0 || temp > 255) {
- bb_error_msg("\"%s\" is invalid lladdr.", arg);
- return -1;
- }
- lladdr[i] = temp;
- if (!cp) {
- break;
- }
- arg = cp;
+ for (i = 0; i < len; i++) {
+ int temp;
+ char *cp = strchr(arg, ':');
+ if (cp) {
+ *cp = 0;
+ cp++;
+ }
+ if (sscanf(arg, "%x", &temp) != 1 || (temp < 0 || temp > 255)) {
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
+ return -1;
+ }
+ lladdr[i] = temp;
+ if (!cp) {
+ break;
}
- return i+1;
+ arg = cp;
}
+ return i+1;
}
diff --git a/release/src/router/busybox/networking/libiproute/ll_map.c b/release/src/router/busybox/networking/libiproute/ll_map.c
index b7a82842..2ed7fbbb 100644
--- a/release/src/router/busybox/networking/libiproute/ll_map.c
+++ b/release/src/router/busybox/networking/libiproute/ll_map.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* ll_map.c
*
@@ -10,27 +11,37 @@
*
*/
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <netinet/in.h>
+#include <net/if.h> /* struct ifreq and co. */
+#include "libbb.h"
#include "libnetlink.h"
-
-struct idxmap
-{
- struct idxmap * next;
- int index;
- int type;
- int alen;
- unsigned flags;
- unsigned char addr[8];
- char name[16];
+#include "ll_map.h"
+
+struct idxmap {
+ struct idxmap *next;
+ int index;
+ int type;
+ int alen;
+ unsigned flags;
+ unsigned char addr[8];
+ char name[16];
};
static struct idxmap *idxmap[16];
-int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static struct idxmap *find_by_index(int idx)
+{
+ struct idxmap *im;
+
+ for (im = idxmap[idx & 0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im;
+ return NULL;
+}
+
+int ll_remember_index(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n,
+ void *arg UNUSED_PARAM)
{
int h;
struct ifinfomsg *ifi = NLMSG_DATA(n);
@@ -43,33 +54,28 @@ int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
return -1;
-
memset(tb, 0, sizeof(tb));
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
if (tb[IFLA_IFNAME] == NULL)
return 0;
- h = ifi->ifi_index&0xF;
+ h = ifi->ifi_index & 0xF;
- for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
+ for (imp = &idxmap[h]; (im = *imp) != NULL; imp = &im->next)
if (im->index == ifi->ifi_index)
- break;
-
- if (im == NULL) {
- im = malloc(sizeof(*im));
- if (im == NULL)
- return 0;
- im->next = *imp;
- im->index = ifi->ifi_index;
- *imp = im;
- }
+ goto found;
+ im = xmalloc(sizeof(*im));
+ im->next = *imp;
+ im->index = ifi->ifi_index;
+ *imp = im;
+ found:
im->type = ifi->ifi_type;
im->flags = ifi->ifi_flags;
if (tb[IFLA_ADDRESS]) {
int alen;
im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
- if (alen > sizeof(im->addr))
+ if (alen > (int)sizeof(im->addr))
alen = sizeof(im->addr);
memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
} else {
@@ -86,9 +92,9 @@ const char *ll_idx_n2a(int idx, char *buf)
if (idx == 0)
return "*";
- for (im = idxmap[idx&0xF]; im; im = im->next)
- if (im->index == idx)
- return im->name;
+ im = find_by_index(idx);
+ if (im)
+ return im->name;
snprintf(buf, 16, "if%d", idx);
return buf;
}
@@ -101,17 +107,19 @@ const char *ll_index_to_name(int idx)
return ll_idx_n2a(idx, nbuf);
}
+#ifdef UNUSED
int ll_index_to_type(int idx)
{
struct idxmap *im;
if (idx == 0)
return -1;
- for (im = idxmap[idx&0xF]; im; im = im->next)
- if (im->index == idx)
- return im->type;
+ im = find_by_index(idx);
+ if (im)
+ return im->type;
return -1;
}
+#endif
unsigned ll_index_to_flags(int idx)
{
@@ -119,46 +127,74 @@ unsigned ll_index_to_flags(int idx)
if (idx == 0)
return 0;
-
- for (im = idxmap[idx&0xF]; im; im = im->next)
- if (im->index == idx)
- return im->flags;
+ im = find_by_index(idx);
+ if (im)
+ return im->flags;
return 0;
}
-int ll_name_to_index(char *name)
+int xll_name_to_index(const char *const name)
{
+ int ret = 0;
+ int sock_fd;
+
+/* caching is not warranted - no users which repeatedly call it */
+#ifdef UNUSED
static char ncache[16];
static int icache;
+
struct idxmap *im;
int i;
if (name == NULL)
- return 0;
- if (icache && strcmp(name, ncache) == 0)
- return icache;
- for (i=0; i<16; i++) {
+ goto out;
+ if (icache && strcmp(name, ncache) == 0) {
+ ret = icache;
+ goto out;
+ }
+ for (i = 0; i < 16; i++) {
for (im = idxmap[i]; im; im = im->next) {
if (strcmp(im->name, name) == 0) {
icache = im->index;
strcpy(ncache, name);
- return im->index;
+ ret = im->index;
+ goto out;
}
}
}
- return 0;
+ /* We have not found the interface in our cache, but the kernel
+ * may still know about it. One reason is that we may be using
+ * module on-demand loading, which means that the kernel will
+ * load the module and make the interface exist only when
+ * we explicitely request it (check for dev_load() in net/core/dev.c).
+ * I can think of other similar scenario, but they are less common...
+ * Jean II */
+#endif
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd >= 0) {
+ struct ifreq ifr;
+ int tmp;
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, name);
+ ifr.ifr_ifindex = -1;
+ tmp = ioctl(sock_fd, SIOCGIFINDEX, &ifr);
+ close(sock_fd);
+ if (tmp >= 0)
+ /* In theory, we should redump the interface list
+ * to update our cache, this is left as an exercise
+ * to the reader... Jean II */
+ ret = ifr.ifr_ifindex;
+ }
+/* out:*/
+ if (ret <= 0)
+ bb_error_msg_and_die("cannot find device \"%s\"", name);
+ return ret;
}
int ll_init_map(struct rtnl_handle *rth)
{
- if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
- perror("Cannot send dump request");
- exit(1);
- }
-
- if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
- fprintf(stderr, "Dump terminated\n");
- exit(1);
- }
+ xrtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK);
+ xrtnl_dump_filter(rth, ll_remember_index, &idxmap);
return 0;
}
diff --git a/release/src/router/busybox/networking/libiproute/ll_map.h b/release/src/router/busybox/networking/libiproute/ll_map.h
index 739f157e..3966def1 100644
--- a/release/src/router/busybox/networking/libiproute/ll_map.h
+++ b/release/src/router/busybox/networking/libiproute/ll_map.h
@@ -1,12 +1,17 @@
-#ifndef __LL_MAP_H__
-#define __LL_MAP_H__ 1
-
-extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
-extern int ll_init_map(struct rtnl_handle *rth);
-extern int ll_name_to_index(char *name);
-extern const char *ll_index_to_name(int idx);
-extern const char *ll_idx_n2a(int idx, char *buf);
-extern int ll_index_to_type(int idx);
-extern unsigned ll_index_to_flags(int idx);
-
-#endif /* __LL_MAP_H__ */
+/* vi: set sw=4 ts=4: */
+#ifndef LL_MAP_H
+#define LL_MAP_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+int ll_remember_index(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+int ll_init_map(struct rtnl_handle *rth);
+int xll_name_to_index(const char *const name);
+const char *ll_index_to_name(int idx);
+const char *ll_idx_n2a(int idx, char *buf);
+/* int ll_index_to_type(int idx); */
+unsigned ll_index_to_flags(int idx);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/ll_proto.c b/release/src/router/busybox/networking/libiproute/ll_proto.c
index 45afdb82..a9349353 100644
--- a/release/src/router/busybox/networking/libiproute/ll_proto.c
+++ b/release/src/router/busybox/networking/libiproute/ll_proto.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* ll_proto.c
*
@@ -9,79 +10,86 @@
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*/
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <string.h>
+#include "libbb.h"
+#include "rt_names.h"
#include "utils.h"
-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
#include <net/ethernet.h>
#else
#include <linux/if_ether.h>
#endif
+#if !ENABLE_WERROR
+#warning de-bloat
+#endif
+/* Before re-enabling this, please (1) conditionalize exotic protocols
+ * on CONFIG_something, and (2) decouple strings and numbers
+ * (use llproto_ids[] = n,n,n..; and llproto_names[] = "loop\0" "pup\0" ...;)
+ */
+
#define __PF(f,n) { ETH_P_##f, #n },
static struct {
int id;
- char *name;
+ const char *name;
} llproto_names[] = {
__PF(LOOP,loop)
-__PF(PUP,pup)
+__PF(PUP,pup)
#ifdef ETH_P_PUPAT
-__PF(PUPAT,pupat)
+__PF(PUPAT,pupat)
#endif
__PF(IP,ip)
__PF(X25,x25)
__PF(ARP,arp)
__PF(BPQ,bpq)
#ifdef ETH_P_IEEEPUP
-__PF(IEEEPUP,ieeepup)
+__PF(IEEEPUP,ieeepup)
#endif
#ifdef ETH_P_IEEEPUPAT
-__PF(IEEEPUPAT,ieeepupat)
+__PF(IEEEPUPAT,ieeepupat)
#endif
-__PF(DEC,dec)
-__PF(DNA_DL,dna_dl)
-__PF(DNA_RC,dna_rc)
-__PF(DNA_RT,dna_rt)
-__PF(LAT,lat)
-__PF(DIAG,diag)
-__PF(CUST,cust)
-__PF(SCA,sca)
-__PF(RARP,rarp)
-__PF(ATALK,atalk)
-__PF(AARP,aarp)
-__PF(IPX,ipx)
-__PF(IPV6,ipv6)
+__PF(DEC,dec)
+__PF(DNA_DL,dna_dl)
+__PF(DNA_RC,dna_rc)
+__PF(DNA_RT,dna_rt)
+__PF(LAT,lat)
+__PF(DIAG,diag)
+__PF(CUST,cust)
+__PF(SCA,sca)
+__PF(RARP,rarp)
+__PF(ATALK,atalk)
+__PF(AARP,aarp)
+__PF(IPX,ipx)
+__PF(IPV6,ipv6)
#ifdef ETH_P_PPP_DISC
-__PF(PPP_DISC,ppp_disc)
+__PF(PPP_DISC,ppp_disc)
#endif
#ifdef ETH_P_PPP_SES
-__PF(PPP_SES,ppp_ses)
+__PF(PPP_SES,ppp_ses)
#endif
#ifdef ETH_P_ATMMPOA
-__PF(ATMMPOA,atmmpoa)
+__PF(ATMMPOA,atmmpoa)
#endif
#ifdef ETH_P_ATMFATE
-__PF(ATMFATE,atmfate)
+__PF(ATMFATE,atmfate)
#endif
-__PF(802_3,802_3)
-__PF(AX25,ax25)
-__PF(ALL,all)
-__PF(802_2,802_2)
-__PF(SNAP,snap)
-__PF(DDCMP,ddcmp)
-__PF(WAN_PPP,wan_ppp)
-__PF(PPP_MP,ppp_mp)
-__PF(LOCALTALK,localtalk)
-__PF(PPPTALK,ppptalk)
-__PF(TR_802_2,tr_802_2)
-__PF(MOBITEX,mobitex)
-__PF(CONTROL,control)
-__PF(IRDA,irda)
+__PF(802_3,802_3)
+__PF(AX25,ax25)
+__PF(ALL,all)
+__PF(802_2,802_2)
+__PF(SNAP,snap)
+__PF(DDCMP,ddcmp)
+__PF(WAN_PPP,wan_ppp)
+__PF(PPP_MP,ppp_mp)
+__PF(LOCALTALK,localtalk)
+__PF(PPPTALK,ppptalk)
+__PF(TR_802_2,tr_802_2)
+__PF(MOBITEX,mobitex)
+__PF(CONTROL,control)
+__PF(IRDA,irda)
#ifdef ETH_P_ECONET
-__PF(ECONET,econet)
+__PF(ECONET,econet)
#endif
{ 0x8100, "802.1Q" },
@@ -90,31 +98,32 @@ __PF(ECONET,econet)
#undef __PF
-char * ll_proto_n2a(unsigned short id, char *buf, int len)
+const char *ll_proto_n2a(unsigned short id, char *buf, int len)
{
- int i;
-
+ unsigned i;
id = ntohs(id);
-
- for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
- if (llproto_names[i].id == id)
+ for (i = 0; i < ARRAY_SIZE(llproto_names); i++) {
+ if (llproto_names[i].id == id)
return llproto_names[i].name;
}
- snprintf(buf, len, "[%d]", id);
- return buf;
+ snprintf(buf, len, "[%d]", id);
+ return buf;
}
int ll_proto_a2n(unsigned short *id, char *buf)
{
- int i;
- for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
- if (strcasecmp(llproto_names[i].name, buf) == 0) {
- *id = htons(llproto_names[i].id);
- return 0;
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE(llproto_names); i++) {
+ if (strcasecmp(llproto_names[i].name, buf) == 0) {
+ i = llproto_names[i].id;
+ goto good;
}
}
- if (get_u16(id, buf, 0))
+ i = bb_strtou(buf, NULL, 0);
+ if (errno || i > 0xffff)
return -1;
- *id = htons(*id);
+ good:
+ *id = htons(i);
return 0;
}
+
diff --git a/release/src/router/busybox/networking/libiproute/ll_types.c b/release/src/router/busybox/networking/libiproute/ll_types.c
index f39f777e..d5d2a1f3 100644
--- a/release/src/router/busybox/networking/libiproute/ll_types.c
+++ b/release/src/router/busybox/networking/libiproute/ll_types.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* ll_types.c
*
@@ -8,108 +9,197 @@
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*/
-#include <stdio.h>
#include <arpa/inet.h>
-
#include <linux/if_arp.h>
-char * ll_type_n2a(int type, char *buf, int len)
+#include "libbb.h"
+#include "rt_names.h"
+
+const char *ll_type_n2a(int type, char *buf, int len)
{
-#define __PF(f,n) { ARPHRD_##f, #n },
-static struct {
- int type;
- char *name;
-} arphrd_names[] = {
-{ 0, "generic" },
-__PF(ETHER,ether)
-__PF(EETHER,eether)
-__PF(AX25,ax25)
-__PF(PRONET,pronet)
-__PF(CHAOS,chaos)
+ static const char arphrd_name[] =
+ /* 0, */ "generic" "\0"
+ /* ARPHRD_LOOPBACK, */ "loopback" "\0"
+ /* ARPHRD_ETHER, */ "ether" "\0"
+#ifdef ARPHRD_INFINIBAND
+ /* ARPHRD_INFINIBAND, */ "infiniband" "\0"
+#endif
#ifdef ARPHRD_IEEE802_TR
-__PF(IEEE802,ieee802)
+ /* ARPHRD_IEEE802, */ "ieee802" "\0"
+ /* ARPHRD_IEEE802_TR, */ "tr" "\0"
#else
-__PF(IEEE802,tr)
+ /* ARPHRD_IEEE802, */ "tr" "\0"
#endif
-__PF(ARCNET,arcnet)
-__PF(APPLETLK,atalk)
-__PF(DLCI,dlci)
-#ifdef ARPHRD_ATM
-__PF(ATM,atm)
+#ifdef ARPHRD_IEEE80211
+ /* ARPHRD_IEEE80211, */ "ieee802.11" "\0"
#endif
-__PF(METRICOM,metricom)
#ifdef ARPHRD_IEEE1394
-__PF(IEEE1394,ieee1394)
+ /* ARPHRD_IEEE1394, */ "ieee1394" "\0"
+#endif
+ /* ARPHRD_IRDA, */ "irda" "\0"
+ /* ARPHRD_SLIP, */ "slip" "\0"
+ /* ARPHRD_CSLIP, */ "cslip" "\0"
+ /* ARPHRD_SLIP6, */ "slip6" "\0"
+ /* ARPHRD_CSLIP6, */ "cslip6" "\0"
+ /* ARPHRD_PPP, */ "ppp" "\0"
+ /* ARPHRD_TUNNEL, */ "ipip" "\0"
+ /* ARPHRD_TUNNEL6, */ "tunnel6" "\0"
+ /* ARPHRD_SIT, */ "sit" "\0"
+ /* ARPHRD_IPGRE, */ "gre" "\0"
+#ifdef ARPHRD_VOID
+ /* ARPHRD_VOID, */ "void" "\0"
#endif
-__PF(SLIP,slip)
-__PF(CSLIP,cslip)
-__PF(SLIP6,slip6)
-__PF(CSLIP6,cslip6)
-__PF(RSRVD,rsrvd)
-__PF(ADAPT,adapt)
-__PF(ROSE,rose)
-__PF(X25,x25)
+#if ENABLE_FEATURE_IP_RARE_PROTOCOLS
+ /* ARPHRD_EETHER, */ "eether" "\0"
+ /* ARPHRD_AX25, */ "ax25" "\0"
+ /* ARPHRD_PRONET, */ "pronet" "\0"
+ /* ARPHRD_CHAOS, */ "chaos" "\0"
+ /* ARPHRD_ARCNET, */ "arcnet" "\0"
+ /* ARPHRD_APPLETLK, */ "atalk" "\0"
+ /* ARPHRD_DLCI, */ "dlci" "\0"
+#ifdef ARPHRD_ATM
+ /* ARPHRD_ATM, */ "atm" "\0"
+#endif
+ /* ARPHRD_METRICOM, */ "metricom" "\0"
+ /* ARPHRD_RSRVD, */ "rsrvd" "\0"
+ /* ARPHRD_ADAPT, */ "adapt" "\0"
+ /* ARPHRD_ROSE, */ "rose" "\0"
+ /* ARPHRD_X25, */ "x25" "\0"
#ifdef ARPHRD_HWX25
-__PF(HWX25,hwx25)
+ /* ARPHRD_HWX25, */ "hwx25" "\0"
#endif
-__PF(PPP,ppp)
-__PF(HDLC,hdlc)
-__PF(LAPB,lapb)
+ /* ARPHRD_HDLC, */ "hdlc" "\0"
+ /* ARPHRD_LAPB, */ "lapb" "\0"
#ifdef ARPHRD_DDCMP
-__PF(DDCMP,ddcmp)
-__PF(RAWHDLC,rawhdlc)
+ /* ARPHRD_DDCMP, */ "ddcmp" "\0"
+ /* ARPHRD_RAWHDLC, */ "rawhdlc" "\0"
#endif
+ /* ARPHRD_FRAD, */ "frad" "\0"
+ /* ARPHRD_SKIP, */ "skip" "\0"
+ /* ARPHRD_LOCALTLK, */ "ltalk" "\0"
+ /* ARPHRD_FDDI, */ "fddi" "\0"
+ /* ARPHRD_BIF, */ "bif" "\0"
+ /* ARPHRD_IPDDP, */ "ip/ddp" "\0"
+ /* ARPHRD_PIMREG, */ "pimreg" "\0"
+ /* ARPHRD_HIPPI, */ "hippi" "\0"
+ /* ARPHRD_ASH, */ "ash" "\0"
+ /* ARPHRD_ECONET, */ "econet" "\0"
+ /* ARPHRD_FCPP, */ "fcpp" "\0"
+ /* ARPHRD_FCAL, */ "fcal" "\0"
+ /* ARPHRD_FCPL, */ "fcpl" "\0"
+ /* ARPHRD_FCFABRIC, */ "fcfb0" "\0"
+ /* ARPHRD_FCFABRIC+1, */ "fcfb1" "\0"
+ /* ARPHRD_FCFABRIC+2, */ "fcfb2" "\0"
+ /* ARPHRD_FCFABRIC+3, */ "fcfb3" "\0"
+ /* ARPHRD_FCFABRIC+4, */ "fcfb4" "\0"
+ /* ARPHRD_FCFABRIC+5, */ "fcfb5" "\0"
+ /* ARPHRD_FCFABRIC+6, */ "fcfb6" "\0"
+ /* ARPHRD_FCFABRIC+7, */ "fcfb7" "\0"
+ /* ARPHRD_FCFABRIC+8, */ "fcfb8" "\0"
+ /* ARPHRD_FCFABRIC+9, */ "fcfb9" "\0"
+ /* ARPHRD_FCFABRIC+10, */ "fcfb10" "\0"
+ /* ARPHRD_FCFABRIC+11, */ "fcfb11" "\0"
+ /* ARPHRD_FCFABRIC+12, */ "fcfb12" "\0"
+#endif /* FEATURE_IP_RARE_PROTOCOLS */
+ ;
-__PF(TUNNEL,ipip)
-__PF(TUNNEL6,tunnel6)
-__PF(FRAD,frad)
-__PF(SKIP,skip)
-__PF(LOOPBACK,loopback)
-__PF(LOCALTLK,ltalk)
-__PF(FDDI,fddi)
-__PF(BIF,bif)
-__PF(SIT,sit)
-__PF(IPDDP,ip/ddp)
-__PF(IPGRE,gre)
-__PF(PIMREG,pimreg)
-__PF(HIPPI,hippi)
-__PF(ASH,ash)
-__PF(ECONET,econet)
-__PF(IRDA,irda)
-__PF(FCPP,fcpp)
-__PF(FCAL,fcal)
-__PF(FCPL,fcpl)
-__PF(FCFABRIC,fcfb0)
-__PF(FCFABRIC+1,fcfb1)
-__PF(FCFABRIC+2,fcfb2)
-__PF(FCFABRIC+3,fcfb3)
-__PF(FCFABRIC+4,fcfb4)
-__PF(FCFABRIC+5,fcfb5)
-__PF(FCFABRIC+6,fcfb6)
-__PF(FCFABRIC+7,fcfb7)
-__PF(FCFABRIC+8,fcfb8)
-__PF(FCFABRIC+9,fcfb9)
-__PF(FCFABRIC+10,fcfb10)
-__PF(FCFABRIC+11,fcfb11)
-__PF(FCFABRIC+12,fcfb12)
+ /* Keep these arrays in sync! */
+
+ static const uint16_t arphrd_type[] = {
+ 0, /* "generic" "\0" */
+ ARPHRD_LOOPBACK, /* "loopback" "\0" */
+ ARPHRD_ETHER, /* "ether" "\0" */
+#ifdef ARPHRD_INFINIBAND
+ ARPHRD_INFINIBAND, /* "infiniband" "\0" */
+#endif
#ifdef ARPHRD_IEEE802_TR
-__PF(IEEE802_TR,tr)
+ ARPHRD_IEEE802, /* "ieee802" "\0" */
+ ARPHRD_IEEE802_TR, /* "tr" "\0" */
+#else
+ ARPHRD_IEEE802, /* "tr" "\0" */
#endif
#ifdef ARPHRD_IEEE80211
-__PF(IEEE80211,ieee802.11)
+ ARPHRD_IEEE80211, /* "ieee802.11" "\0" */
+#endif
+#ifdef ARPHRD_IEEE1394
+ ARPHRD_IEEE1394, /* "ieee1394" "\0" */
#endif
+ ARPHRD_IRDA, /* "irda" "\0" */
+ ARPHRD_SLIP, /* "slip" "\0" */
+ ARPHRD_CSLIP, /* "cslip" "\0" */
+ ARPHRD_SLIP6, /* "slip6" "\0" */
+ ARPHRD_CSLIP6, /* "cslip6" "\0" */
+ ARPHRD_PPP, /* "ppp" "\0" */
+ ARPHRD_TUNNEL, /* "ipip" "\0" */
+ ARPHRD_TUNNEL6, /* "tunnel6" "\0" */
+ ARPHRD_SIT, /* "sit" "\0" */
+ ARPHRD_IPGRE, /* "gre" "\0" */
#ifdef ARPHRD_VOID
-__PF(VOID,void)
+ ARPHRD_VOID, /* "void" "\0" */
+#endif
+
+#if ENABLE_FEATURE_IP_RARE_PROTOCOLS
+ ARPHRD_EETHER, /* "eether" "\0" */
+ ARPHRD_AX25, /* "ax25" "\0" */
+ ARPHRD_PRONET, /* "pronet" "\0" */
+ ARPHRD_CHAOS, /* "chaos" "\0" */
+ ARPHRD_ARCNET, /* "arcnet" "\0" */
+ ARPHRD_APPLETLK, /* "atalk" "\0" */
+ ARPHRD_DLCI, /* "dlci" "\0" */
+#ifdef ARPHRD_ATM
+ ARPHRD_ATM, /* "atm" "\0" */
+#endif
+ ARPHRD_METRICOM, /* "metricom" "\0" */
+ ARPHRD_RSRVD, /* "rsrvd" "\0" */
+ ARPHRD_ADAPT, /* "adapt" "\0" */
+ ARPHRD_ROSE, /* "rose" "\0" */
+ ARPHRD_X25, /* "x25" "\0" */
+#ifdef ARPHRD_HWX25
+ ARPHRD_HWX25, /* "hwx25" "\0" */
+#endif
+ ARPHRD_HDLC, /* "hdlc" "\0" */
+ ARPHRD_LAPB, /* "lapb" "\0" */
+#ifdef ARPHRD_DDCMP
+ ARPHRD_DDCMP, /* "ddcmp" "\0" */
+ ARPHRD_RAWHDLC, /* "rawhdlc" "\0" */
#endif
-};
-#undef __PF
+ ARPHRD_FRAD, /* "frad" "\0" */
+ ARPHRD_SKIP, /* "skip" "\0" */
+ ARPHRD_LOCALTLK, /* "ltalk" "\0" */
+ ARPHRD_FDDI, /* "fddi" "\0" */
+ ARPHRD_BIF, /* "bif" "\0" */
+ ARPHRD_IPDDP, /* "ip/ddp" "\0" */
+ ARPHRD_PIMREG, /* "pimreg" "\0" */
+ ARPHRD_HIPPI, /* "hippi" "\0" */
+ ARPHRD_ASH, /* "ash" "\0" */
+ ARPHRD_ECONET, /* "econet" "\0" */
+ ARPHRD_FCPP, /* "fcpp" "\0" */
+ ARPHRD_FCAL, /* "fcal" "\0" */
+ ARPHRD_FCPL, /* "fcpl" "\0" */
+ ARPHRD_FCFABRIC, /* "fcfb0" "\0" */
+ ARPHRD_FCFABRIC+1, /* "fcfb1" "\0" */
+ ARPHRD_FCFABRIC+2, /* "fcfb2" "\0" */
+ ARPHRD_FCFABRIC+3, /* "fcfb3" "\0" */
+ ARPHRD_FCFABRIC+4, /* "fcfb4" "\0" */
+ ARPHRD_FCFABRIC+5, /* "fcfb5" "\0" */
+ ARPHRD_FCFABRIC+6, /* "fcfb6" "\0" */
+ ARPHRD_FCFABRIC+7, /* "fcfb7" "\0" */
+ ARPHRD_FCFABRIC+8, /* "fcfb8" "\0" */
+ ARPHRD_FCFABRIC+9, /* "fcfb9" "\0" */
+ ARPHRD_FCFABRIC+10, /* "fcfb10" "\0" */
+ ARPHRD_FCFABRIC+11, /* "fcfb11" "\0" */
+ ARPHRD_FCFABRIC+12, /* "fcfb12" "\0" */
+#endif /* FEATURE_IP_RARE_PROTOCOLS */
+ };
- int i;
- for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
- if (arphrd_names[i].type == type)
- return arphrd_names[i].name;
+ unsigned i;
+ const char *aname = arphrd_name;
+ for (i = 0; i < ARRAY_SIZE(arphrd_type); i++) {
+ if (arphrd_type[i] == type)
+ return aname;
+ aname += strlen(aname) + 1;
}
- snprintf(buf, len, "[%d]", type);
- return buf;
+ snprintf(buf, len, "[%d]", type);
+ return buf;
}
diff --git a/release/src/router/busybox/networking/libiproute/rt_names.c b/release/src/router/busybox/networking/libiproute/rt_names.c
index d503645b..e4d10613 100644
--- a/release/src/router/busybox/networking/libiproute/rt_names.c
+++ b/release/src/router/busybox/networking/libiproute/rt_names.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* rt_names.c rtnetlink names DB.
*
@@ -8,84 +9,66 @@
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
+#include "libbb.h"
+#include "rt_names.h"
-static void rtnl_tab_initialize(char *file, char **tab, int size)
+/* so far all callers have size == 256 */
+#define rtnl_tab_initialize(file, tab, size) rtnl_tab_initialize(file, tab)
+#define size 256
+static void rtnl_tab_initialize(const char *file, const char **tab, int size)
{
- char buf[512];
- FILE *fp;
-
- fp = fopen(file, "r");
- if (!fp)
- return;
- while (fgets(buf, sizeof(buf), fp)) {
- char *p = buf;
- int id;
- char namebuf[512];
-
- while (*p == ' ' || *p == '\t')
- p++;
- if (*p == '#' || *p == '\n' || *p == 0)
- continue;
- if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 &&
- sscanf(p, "0x%x %s #", &id, namebuf) != 2 &&
- sscanf(p, "%d %s\n", &id, namebuf) != 2 &&
- sscanf(p, "%d %s #", &id, namebuf) != 2) {
- fprintf(stderr, "Database %s is corrupted at %s\n",
- file, p);
- return;
+ char *token[2];
+ parser_t *parser = config_open2(file, fopen_for_read);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+ int id = bb_strtou(token[0], NULL, 0);
+ if (id < 0 || id > size) {
+ bb_error_msg("database %s is corrupted at line %d",
+ file, parser->lineno);
+ break;
}
-
- if (id<0 || id>size)
- continue;
-
- tab[id] = strdup(namebuf);
+ tab[id] = xstrdup(token[1]);
}
- fclose(fp);
+ config_close(parser);
}
+#undef size
-
-static char * rtnl_rtprot_tab[256] = {
- "none",
- "redirect",
- "kernel",
- "boot",
- "static",
- NULL,
- NULL,
- NULL,
- "gated",
- "ra",
- "mrt",
- "zebra",
- "bird",
-};
-
-
-
-static int rtnl_rtprot_init;
+static const char **rtnl_rtprot_tab; /* [256] */
static void rtnl_rtprot_initialize(void)
{
- rtnl_rtprot_init = 1;
+ static const char *const init_tab[] = {
+ "none",
+ "redirect",
+ "kernel",
+ "boot",
+ "static",
+ NULL,
+ NULL,
+ NULL,
+ "gated",
+ "ra",
+ "mrt",
+ "zebra",
+ "bird",
+ };
+ if (rtnl_rtprot_tab) return;
+ rtnl_rtprot_tab = xzalloc(256 * sizeof(rtnl_rtprot_tab[0]));
+ memcpy(rtnl_rtprot_tab, init_tab, sizeof(init_tab));
rtnl_tab_initialize("/etc/iproute2/rt_protos",
rtnl_rtprot_tab, 256);
}
-char * rtnl_rtprot_n2a(int id, char *buf, int len)
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len)
{
- if (id<0 || id>=256) {
+ if (id < 0 || id >= 256) {
snprintf(buf, len, "%d", id);
return buf;
}
- if (!rtnl_rtprot_tab[id]) {
- if (!rtnl_rtprot_init)
- rtnl_rtprot_initialize();
- }
+
+ rtnl_rtprot_initialize();
+
if (rtnl_rtprot_tab[id])
return rtnl_rtprot_tab[id];
snprintf(buf, len, "%d", id);
@@ -94,9 +77,8 @@ char * rtnl_rtprot_n2a(int id, char *buf, int len)
int rtnl_rtprot_a2n(uint32_t *id, char *arg)
{
- static char *cache = NULL;
+ static const char *cache = NULL;
static unsigned long res;
- char *end;
int i;
if (cache && strcmp(cache, arg) == 0) {
@@ -104,10 +86,9 @@ int rtnl_rtprot_a2n(uint32_t *id, char *arg)
return 0;
}
- if (!rtnl_rtprot_init)
- rtnl_rtprot_initialize();
+ rtnl_rtprot_initialize();
- for (i=0; i<256; i++) {
+ for (i = 0; i < 256; i++) {
if (rtnl_rtprot_tab[i] &&
strcmp(rtnl_rtprot_tab[i], arg) == 0) {
cache = rtnl_rtprot_tab[i];
@@ -117,24 +98,21 @@ int rtnl_rtprot_a2n(uint32_t *id, char *arg)
}
}
- res = strtoul(arg, &end, 0);
- if (!end || end == arg || *end || res > 255)
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
return -1;
*id = res;
return 0;
}
-
-static char * rtnl_rtscope_tab[256] = {
- "global",
-};
-
-static int rtnl_rtscope_init;
+static const char **rtnl_rtscope_tab; /* [256] */
static void rtnl_rtscope_initialize(void)
{
- rtnl_rtscope_init = 1;
+ if (rtnl_rtscope_tab) return;
+ rtnl_rtscope_tab = xzalloc(256 * sizeof(rtnl_rtscope_tab[0]));
+ rtnl_rtscope_tab[0] = "global";
rtnl_rtscope_tab[255] = "nowhere";
rtnl_rtscope_tab[254] = "host";
rtnl_rtscope_tab[253] = "link";
@@ -143,16 +121,16 @@ static void rtnl_rtscope_initialize(void)
rtnl_rtscope_tab, 256);
}
-char * rtnl_rtscope_n2a(int id, char *buf, int len)
+
+const char* rtnl_rtscope_n2a(int id, char *buf, int len)
{
- if (id<0 || id>=256) {
+ if (id < 0 || id >= 256) {
snprintf(buf, len, "%d", id);
return buf;
}
- if (!rtnl_rtscope_tab[id]) {
- if (!rtnl_rtscope_init)
- rtnl_rtscope_initialize();
- }
+
+ rtnl_rtscope_initialize();
+
if (rtnl_rtscope_tab[id])
return rtnl_rtscope_tab[id];
snprintf(buf, len, "%d", id);
@@ -161,9 +139,8 @@ char * rtnl_rtscope_n2a(int id, char *buf, int len)
int rtnl_rtscope_a2n(uint32_t *id, char *arg)
{
- static char *cache = NULL;
+ static const char *cache = NULL;
static unsigned long res;
- char *end;
int i;
if (cache && strcmp(cache, arg) == 0) {
@@ -171,10 +148,9 @@ int rtnl_rtscope_a2n(uint32_t *id, char *arg)
return 0;
}
- if (!rtnl_rtscope_init)
- rtnl_rtscope_initialize();
+ rtnl_rtscope_initialize();
- for (i=0; i<256; i++) {
+ for (i = 0; i < 256; i++) {
if (rtnl_rtscope_tab[i] &&
strcmp(rtnl_rtscope_tab[i], arg) == 0) {
cache = rtnl_rtscope_tab[i];
@@ -184,50 +160,30 @@ int rtnl_rtscope_a2n(uint32_t *id, char *arg)
}
}
- res = strtoul(arg, &end, 0);
- if (!end || end == arg || *end || res > 255)
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
return -1;
*id = res;
return 0;
}
-
-static char * rtnl_rtrealm_tab[256] = {
- "unknown",
-};
-
-static int rtnl_rtrealm_init;
+static const char **rtnl_rtrealm_tab; /* [256] */
static void rtnl_rtrealm_initialize(void)
{
- rtnl_rtrealm_init = 1;
+ if (rtnl_rtrealm_tab) return;
+ rtnl_rtrealm_tab = xzalloc(256 * sizeof(rtnl_rtrealm_tab[0]));
+ rtnl_rtrealm_tab[0] = "unknown";
rtnl_tab_initialize("/etc/iproute2/rt_realms",
rtnl_rtrealm_tab, 256);
}
-char * rtnl_rtrealm_n2a(int id, char *buf, int len)
-{
- if (id<0 || id>=256) {
- snprintf(buf, len, "%d", id);
- return buf;
- }
- if (!rtnl_rtrealm_tab[id]) {
- if (!rtnl_rtrealm_init)
- rtnl_rtrealm_initialize();
- }
- if (rtnl_rtrealm_tab[id])
- return rtnl_rtrealm_tab[id];
- snprintf(buf, len, "%d", id);
- return buf;
-}
-
int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
{
- static char *cache = NULL;
+ static const char *cache = NULL;
static unsigned long res;
- char *end;
int i;
if (cache && strcmp(cache, arg) == 0) {
@@ -235,10 +191,9 @@ int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
return 0;
}
- if (!rtnl_rtrealm_init)
- rtnl_rtrealm_initialize();
+ rtnl_rtrealm_initialize();
- for (i=0; i<256; i++) {
+ for (i = 0; i < 256; i++) {
if (rtnl_rtrealm_tab[i] &&
strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
cache = rtnl_rtrealm_tab[i];
@@ -248,51 +203,63 @@ int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
}
}
- res = strtoul(arg, &end, 0);
- if (!end || end == arg || *end || res > 255)
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
return -1;
*id = res;
return 0;
}
+#if ENABLE_FEATURE_IP_RULE
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtrealm_initialize();
+ if (rtnl_rtrealm_tab[id])
+ return rtnl_rtrealm_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+#endif
-static char * rtnl_rttable_tab[256] = {
- "unspec",
-};
-static int rtnl_rttable_init;
+static const char **rtnl_rtdsfield_tab; /* [256] */
-static void rtnl_rttable_initialize(void)
+static void rtnl_rtdsfield_initialize(void)
{
- rtnl_rttable_init = 1;
- rtnl_rttable_tab[255] = "local";
- rtnl_rttable_tab[254] = "main";
- rtnl_tab_initialize("/etc/iproute2/rt_tables",
- rtnl_rttable_tab, 256);
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rtdsfield_tab = xzalloc(256 * sizeof(rtnl_rtdsfield_tab[0]));
+ rtnl_rtdsfield_tab[0] = "0";
+ rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
}
-char * rtnl_rttable_n2a(int id, char *buf, int len)
+
+const char * rtnl_dsfield_n2a(int id, char *buf, int len)
{
- if (id<0 || id>=256) {
+ if (id < 0 || id >= 256) {
snprintf(buf, len, "%d", id);
return buf;
}
- if (!rtnl_rttable_tab[id]) {
- if (!rtnl_rttable_init)
- rtnl_rttable_initialize();
- }
- if (rtnl_rttable_tab[id])
- return rtnl_rttable_tab[id];
- snprintf(buf, len, "%d", id);
+
+ rtnl_rtdsfield_initialize();
+
+ if (rtnl_rtdsfield_tab[id])
+ return rtnl_rtdsfield_tab[id];
+ snprintf(buf, len, "0x%02x", id);
return buf;
}
-int rtnl_rttable_a2n(uint32_t *id, char *arg)
+
+int rtnl_dsfield_a2n(uint32_t *id, char *arg)
{
- static char *cache = NULL;
+ static const char *cache = NULL;
static unsigned long res;
- char *end;
int i;
if (cache && strcmp(cache, arg) == 0) {
@@ -300,62 +267,60 @@ int rtnl_rttable_a2n(uint32_t *id, char *arg)
return 0;
}
- if (!rtnl_rttable_init)
- rtnl_rttable_initialize();
+ rtnl_rtdsfield_initialize();
- for (i=0; i<256; i++) {
- if (rtnl_rttable_tab[i] &&
- strcmp(rtnl_rttable_tab[i], arg) == 0) {
- cache = rtnl_rttable_tab[i];
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtdsfield_tab[i] &&
+ strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
+ cache = rtnl_rtdsfield_tab[i];
res = i;
*id = res;
return 0;
}
}
- i = strtoul(arg, &end, 0);
- if (!end || end == arg || *end || i > 255)
+ res = bb_strtoul(arg, NULL, 16);
+ if (errno || res > 255)
return -1;
- *id = i;
+ *id = res;
return 0;
}
-static char * rtnl_rtdsfield_tab[256] = {
- "0",
-};
+#if ENABLE_FEATURE_IP_RULE
+static const char **rtnl_rttable_tab; /* [256] */
-static int rtnl_rtdsfield_init;
-
-static void rtnl_rtdsfield_initialize(void)
+static void rtnl_rttable_initialize(void)
{
- rtnl_rtdsfield_init = 1;
- rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
- rtnl_rtdsfield_tab, 256);
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rttable_tab = xzalloc(256 * sizeof(rtnl_rttable_tab[0]));
+ rtnl_rttable_tab[0] = "unspec";
+ rtnl_rttable_tab[255] = "local";
+ rtnl_rttable_tab[254] = "main";
+ rtnl_rttable_tab[253] = "default";
+ rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab, 256);
}
-char * rtnl_dsfield_n2a(int id, char *buf, int len)
+
+const char *rtnl_rttable_n2a(int id, char *buf, int len)
{
- if (id<0 || id>=256) {
+ if (id < 0 || id >= 256) {
snprintf(buf, len, "%d", id);
return buf;
}
- if (!rtnl_rtdsfield_tab[id]) {
- if (!rtnl_rtdsfield_init)
- rtnl_rtdsfield_initialize();
- }
- if (rtnl_rtdsfield_tab[id])
- return rtnl_rtdsfield_tab[id];
- snprintf(buf, len, "0x%02x", id);
+
+ rtnl_rttable_initialize();
+
+ if (rtnl_rttable_tab[id])
+ return rtnl_rttable_tab[id];
+ snprintf(buf, len, "%d", id);
return buf;
}
-
-int rtnl_dsfield_a2n(uint32_t *id, char *arg)
+int rtnl_rttable_a2n(uint32_t * id, char *arg)
{
static char *cache = NULL;
static unsigned long res;
- char *end;
int i;
if (cache && strcmp(cache, arg) == 0) {
@@ -363,23 +328,22 @@ int rtnl_dsfield_a2n(uint32_t *id, char *arg)
return 0;
}
- if (!rtnl_rtdsfield_init)
- rtnl_rtdsfield_initialize();
+ rtnl_rttable_initialize();
- for (i=0; i<256; i++) {
- if (rtnl_rtdsfield_tab[i] &&
- strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
- cache = rtnl_rtdsfield_tab[i];
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rttable_tab[i] && strcmp(rtnl_rttable_tab[i], arg) == 0) {
+ cache = (char*)rtnl_rttable_tab[i];
res = i;
*id = res;
return 0;
}
}
- res = strtoul(arg, &end, 16);
- if (!end || end == arg || *end || res > 255)
+ i = bb_strtoul(arg, NULL, 0);
+ if (errno || i > 255)
return -1;
- *id = res;
+ *id = i;
return 0;
}
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/rt_names.h b/release/src/router/busybox/networking/libiproute/rt_names.h
index 97bc6169..a2d4fd14 100644
--- a/release/src/router/busybox/networking/libiproute/rt_names.h
+++ b/release/src/router/busybox/networking/libiproute/rt_names.h
@@ -1,30 +1,29 @@
-#ifndef RT_NAMES_H_
-#define RT_NAMES_H_ 1
+/* vi: set sw=4 ts=4: */
+#ifndef RT_NAMES_H
+#define RT_NAMES_H 1
-#include <stdint.h>
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
-const char* rtnl_rtprot_n2a(int id, char *buf, int len);
-const char* rtnl_rtscope_n2a(int id, char *buf, int len);
-const char* rtnl_rttable_n2a(int id, char *buf, int len);
-const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
-const char* rtnl_dsfield_n2a(int id, char *buf, int len);
-int rtnl_rtprot_a2n(int *id, char *arg);
-int rtnl_rtscope_a2n(int *id, char *arg);
-int rtnl_rttable_a2n(int *id, char *arg);
-int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
-int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+extern const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+extern const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+extern const char* rtnl_rttable_n2a(int id, char *buf, int len);
+extern int rtnl_rtprot_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtscope_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+extern int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+extern int rtnl_rttable_a2n(uint32_t *id, char *arg);
-const char *inet_proto_n2a(int proto, char *buf, int len);
-int inet_proto_a2n(char *buf);
+extern const char* ll_type_n2a(int type, char *buf, int len);
+extern const char* ll_addr_n2a(unsigned char *addr, int alen, int type,
+ char *buf, int blen);
+extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
-const char * ll_type_n2a(int type, char *buf, int len);
-
-const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
-int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
-
-const char * ll_proto_n2a(unsigned short id, char *buf, int len);
-int ll_proto_a2n(unsigned short *id, char *buf);
+extern const char* ll_proto_n2a(unsigned short id, char *buf, int len);
+extern int ll_proto_a2n(unsigned short *id, char *buf);
+POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/release/src/router/busybox/networking/libiproute/rtm_map.c b/release/src/router/busybox/networking/libiproute/rtm_map.c
index 5f6a9e69..ca2f4436 100644
--- a/release/src/router/busybox/networking/libiproute/rtm_map.c
+++ b/release/src/router/busybox/networking/libiproute/rtm_map.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* rtm_map.c
*
@@ -10,13 +11,11 @@
*
*/
-#include <stdlib.h>
-#include <string.h>
-
+#include "libbb.h"
#include "rt_names.h"
#include "utils.h"
-char *rtnl_rtntype_n2a(int id, char *buf, int len)
+const char *rtnl_rtntype_n2a(int id, char *buf, int len)
{
switch (id) {
case RTN_UNSPEC:
@@ -52,31 +51,40 @@ char *rtnl_rtntype_n2a(int id, char *buf, int len)
int rtnl_rtntype_a2n(int *id, char *arg)
{
+ static const char keywords[] ALIGN1 =
+ "local\0""nat\0""broadcast\0""brd\0""anycast\0"
+ "multicast\0""prohibit\0""unreachable\0""blackhole\0"
+ "xresolve\0""unicast\0""throw\0";
+ enum {
+ ARG_local = 1, ARG_nat, ARG_broadcast, ARG_brd, ARG_anycast,
+ ARG_multicast, ARG_prohibit, ARG_unreachable, ARG_blackhole,
+ ARG_xresolve, ARG_unicast, ARG_throw
+ };
+ const smalluint key = index_in_substrings(keywords, arg) + 1;
char *end;
unsigned long res;
- if (strcmp(arg, "local") == 0)
+ if (key == ARG_local)
res = RTN_LOCAL;
- else if (strcmp(arg, "nat") == 0)
+ else if (key == ARG_nat)
res = RTN_NAT;
- else if (matches(arg, "broadcast") == 0 ||
- strcmp(arg, "brd") == 0)
+ else if (key == ARG_broadcast || key == ARG_brd)
res = RTN_BROADCAST;
- else if (matches(arg, "anycast") == 0)
+ else if (key == ARG_anycast)
res = RTN_ANYCAST;
- else if (matches(arg, "multicast") == 0)
+ else if (key == ARG_multicast)
res = RTN_MULTICAST;
- else if (matches(arg, "prohibit") == 0)
+ else if (key == ARG_prohibit)
res = RTN_PROHIBIT;
- else if (matches(arg, "unreachable") == 0)
+ else if (key == ARG_unreachable)
res = RTN_UNREACHABLE;
- else if (matches(arg, "blackhole") == 0)
+ else if (key == ARG_blackhole)
res = RTN_BLACKHOLE;
- else if (matches(arg, "xresolve") == 0)
+ else if (key == ARG_xresolve)
res = RTN_XRESOLVE;
- else if (matches(arg, "unicast") == 0)
+ else if (key == ARG_unicast)
res = RTN_UNICAST;
- else if (strcmp(arg, "throw") == 0)
+ else if (key == ARG_throw)
res = RTN_THROW;
else {
res = strtoul(arg, &end, 0);
@@ -87,9 +95,9 @@ int rtnl_rtntype_a2n(int *id, char *arg)
return 0;
}
-int get_rt_realms(__u32 *realms, char *arg)
+int get_rt_realms(uint32_t *realms, char *arg)
{
- __u32 realm = 0;
+ uint32_t realm = 0;
char *p = strchr(arg, '/');
*realms = 0;
diff --git a/release/src/router/busybox/networking/libiproute/rtm_map.h b/release/src/router/busybox/networking/libiproute/rtm_map.h
index 70bda7d0..ab1b70e4 100644
--- a/release/src/router/busybox/networking/libiproute/rtm_map.h
+++ b/release/src/router/busybox/networking/libiproute/rtm_map.h
@@ -1,10 +1,14 @@
-#ifndef __RTM_MAP_H__
-#define __RTM_MAP_H__ 1
+/* vi: set sw=4 ts=4: */
+#ifndef RTM_MAP_H
+#define RTM_MAP_H 1
-char *rtnl_rtntype_n2a(int id, char *buf, int len);
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+const char *rtnl_rtntype_n2a(int id, char *buf, int len);
int rtnl_rtntype_a2n(int *id, char *arg);
-int get_rt_realms(__u32 *realms, char *arg);
+int get_rt_realms(uint32_t *realms, char *arg);
+POP_SAVED_FUNCTION_VISIBILITY
-#endif /* __RTM_MAP_H__ */
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/utils.c b/release/src/router/busybox/networking/libiproute/utils.c
index fa154860..c84d018e 100644
--- a/release/src/router/busybox/networking/libiproute/utils.c
+++ b/release/src/router/busybox/networking/libiproute/utils.c
@@ -1,135 +1,70 @@
+/* vi: set sw=4 ts=4: */
/*
* utils.c
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
- *
* Changes:
*
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
*/
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-
-#include "utils.h"
#include "libbb.h"
+#include "utils.h"
+#include "inet_common.h"
-int get_integer(int *val, char *arg, int base)
-{
- long res;
- char *ptr;
-
- if (!arg || !*arg)
- return -1;
- res = strtol(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
- return -1;
- *val = res;
- return 0;
-}
-
-int get_unsigned(unsigned *val, char *arg, int base)
-{
- unsigned long res;
- char *ptr;
-
- if (!arg || !*arg)
- return -1;
- res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
- return -1;
- *val = res;
- return 0;
-}
-
-int get_u32(__u32 * val, char *arg, int base)
+unsigned get_unsigned(char *arg, const char *errmsg)
{
unsigned long res;
char *ptr;
- if (!arg || !*arg)
- return -1;
- res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
- return -1;
- *val = res;
- return 0;
+ if (*arg) {
+ res = strtoul(arg, &ptr, 0);
+ if (!*ptr && res <= UINT_MAX) {
+ return res;
+ }
+ }
+ invarg(arg, errmsg); /* does not return */
}
-int get_u16(__u16 * val, char *arg, int base)
+uint32_t get_u32(char *arg, const char *errmsg)
{
unsigned long res;
char *ptr;
- if (!arg || !*arg)
- return -1;
- res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
- return -1;
- *val = res;
- return 0;
+ if (*arg) {
+ res = strtoul(arg, &ptr, 0);
+ if (!*ptr && res <= 0xFFFFFFFFUL) {
+ return res;
+ }
+ }
+ invarg(arg, errmsg); /* does not return */
}
-int get_u8(__u8 * val, char *arg, int base)
+uint16_t get_u16(char *arg, const char *errmsg)
{
unsigned long res;
char *ptr;
- if (!arg || !*arg)
- return -1;
- res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0xFF)
- return -1;
- *val = res;
- return 0;
-}
-
-int get_s16(__s16 * val, char *arg, int base)
-{
- long res;
- char *ptr;
-
- if (!arg || !*arg)
- return -1;
- res = strtol(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
- return -1;
- *val = res;
- return 0;
-}
-
-int get_s8(__s8 * val, char *arg, int base)
-{
- long res;
- char *ptr;
-
- if (!arg || !*arg)
- return -1;
- res = strtol(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
- return -1;
- *val = res;
- return 0;
+ if (*arg) {
+ res = strtoul(arg, &ptr, 0);
+ if (!*ptr && res <= 0xFFFF) {
+ return res;
+ }
+ }
+ invarg(arg, errmsg); /* does not return */
}
-int get_addr_1(inet_prefix * addr, char *name, int family)
+int get_addr_1(inet_prefix *addr, char *name, int family)
{
- char *cp;
- unsigned char *ap = (unsigned char *) addr->data;
- int i;
-
memset(addr, 0, sizeof(*addr));
- if (strcmp(name, "default") == 0 ||
- strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
+ if (strcmp(name, bb_str_default) == 0
+ || strcmp(name, "all") == 0
+ || strcmp(name, "any") == 0
+ ) {
addr->family = family;
addr->bytelen = (family == AF_INET6 ? 16 : 4);
addr->bitlen = -1;
@@ -150,21 +85,14 @@ int get_addr_1(inet_prefix * addr, char *name, int family)
addr->family = AF_INET;
if (family != AF_UNSPEC && family != AF_INET)
return -1;
+ if (inet_pton(AF_INET, name, addr->data) <= 0)
+ return -1;
addr->bytelen = 4;
addr->bitlen = -1;
- for (cp = name, i = 0; *cp; cp++) {
- if (*cp <= '9' && *cp >= '0') {
- ap[i] = 10 * ap[i] + (*cp - '0');
- continue;
- }
- if (*cp == '.' && ++i <= 3)
- continue;
- return -1;
- }
return 0;
}
-int get_prefix_1(inet_prefix * dst, char *arg, int family)
+static int get_prefix_1(inet_prefix *dst, char *arg, int family)
{
int err;
unsigned plen;
@@ -172,111 +100,114 @@ int get_prefix_1(inet_prefix * dst, char *arg, int family)
memset(dst, 0, sizeof(*dst));
- if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) {
+ if (strcmp(arg, bb_str_default) == 0
+ || strcmp(arg, "all") == 0
+ || strcmp(arg, "any") == 0
+ ) {
dst->family = family;
- dst->bytelen = 0;
- dst->bitlen = 0;
+ /*dst->bytelen = 0; - done by memset */
+ /*dst->bitlen = 0;*/
return 0;
}
slash = strchr(arg, '/');
if (slash)
- *slash = 0;
+ *slash = '\0';
err = get_addr_1(dst, arg, family);
if (err == 0) {
- switch (dst->family) {
- case AF_INET6:
- dst->bitlen = 128;
- break;
- default:
- case AF_INET:
- dst->bitlen = 32;
- }
+ dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
if (slash) {
- if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) {
+ inet_prefix netmask_pfx;
+
+ netmask_pfx.family = AF_UNSPEC;
+ plen = bb_strtou(slash + 1, NULL, 0);
+ if ((errno || plen > dst->bitlen)
+ && (get_addr_1(&netmask_pfx, slash + 1, family)))
err = -1;
- goto done;
+ else if (netmask_pfx.family == AF_INET) {
+ /* fill in prefix length of dotted quad */
+ uint32_t mask = ntohl(netmask_pfx.data[0]);
+ uint32_t host = ~mask;
+
+ /* a valid netmask must be 2^n - 1 */
+ if (!(host & (host + 1))) {
+ for (plen = 0; mask; mask <<= 1)
+ ++plen;
+ if (plen >= 0 && plen <= dst->bitlen) {
+ dst->bitlen = plen;
+ /* dst->flags |= PREFIXLEN_SPECIFIED; */
+ } else
+ err = -1;
+ } else
+ err = -1;
+ } else {
+ /* plain prefix */
+ dst->bitlen = plen;
}
- dst->bitlen = plen;
}
}
- done:
if (slash)
*slash = '/';
return err;
}
-int get_addr(inet_prefix * dst, char *arg, int family)
+int get_addr(inet_prefix *dst, char *arg, int family)
{
if (family == AF_PACKET) {
- bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
+ bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "address");
}
if (get_addr_1(dst, arg, family)) {
- bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
+ bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "address", arg);
}
return 0;
}
-int get_prefix(inet_prefix * dst, char *arg, int family)
+int get_prefix(inet_prefix *dst, char *arg, int family)
{
if (family == AF_PACKET) {
- bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
+ bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix");
}
if (get_prefix_1(dst, arg, family)) {
- bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
+ bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
}
return 0;
}
-__u32 get_addr32(char *name)
+uint32_t get_addr32(char *name)
{
inet_prefix addr;
if (get_addr_1(&addr, name, AF_INET)) {
- bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name);
+ bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "IP", "address", name);
}
return addr.data[0];
}
-void incomplete_command()
+void incomplete_command(void)
{
- bb_error_msg("Command line is not complete. Try option \"help\"");
- exit(-1);
+ bb_error_msg_and_die("command line is not complete, try option \"help\"");
}
-void invarg(char *msg, char *arg)
+void invarg(const char *arg, const char *opt)
{
- bb_error_msg("argument \"%s\" is wrong: %s", arg, msg);
- exit(-1);
+ bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
}
-void duparg(char *key, char *arg)
+void duparg(const char *key, const char *arg)
{
- bb_error_msg("duplicate \"%s\": \"%s\" is the second value.", key, arg);
- exit(-1);
+ bb_error_msg_and_die("duplicate \"%s\": \"%s\" is the second value", key, arg);
}
-void duparg2(char *key, char *arg)
+void duparg2(const char *key, const char *arg)
{
- bb_error_msg("either \"%s\" is duplicate, or \"%s\" is a garbage.", key, arg);
- exit(-1);
+ bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
}
-int matches(char *cmd, char *pattern)
+int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits)
{
- int len = strlen(cmd);
-
- if (len > strlen(pattern)) {
- return -1;
- }
- return memcmp(pattern, cmd, len);
-}
-
-int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
-{
- __u32 *a1 = a->data;
- __u32 *a2 = b->data;
- int words = bits >> 0x05;
+ uint32_t *a1 = a->data;
+ uint32_t *a2 = b->data;
+ int words = bits >> 5;
bits &= 0x1f;
@@ -285,8 +216,8 @@ int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
return -1;
if (bits) {
- __u32 w1, w2;
- __u32 mask;
+ uint32_t w1, w2;
+ uint32_t mask;
w1 = a1[words];
w2 = a2[words];
@@ -300,27 +231,8 @@ int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
return 0;
}
-int __iproute2_hz_internal;
-
-int __get_hz(void)
-{
- int hz = 0;
- FILE *fp = fopen("/proc/net/psched", "r");
-
- if (fp) {
- unsigned nom, denom;
-
- if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
- if (nom == 1000000)
- hz = denom;
- fclose(fp);
- }
- if (hz)
- return hz;
- return sysconf(_SC_CLK_TCK);
-}
-
-const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
+const char *rt_addr_n2a(int af,
+ void *addr, char *buf, int buflen)
{
switch (af) {
case AF_INET:
@@ -331,10 +243,9 @@ const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
}
}
-
+#ifdef RESOLVE_HOSTNAMES
const char *format_host(int af, int len, void *addr, char *buf, int buflen)
{
-#ifdef RESOLVE_HOSTNAMES
if (resolve_hosts) {
struct hostent *h_ent;
@@ -349,11 +260,14 @@ const char *format_host(int af, int len, void *addr, char *buf, int buflen)
default:;
}
}
- if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
- snprintf(buf, buflen - 1, "%s", h_ent->h_name);
- return buf;
+ if (len > 0) {
+ h_ent = gethostbyaddr(addr, len, af);
+ if (h_ent != NULL) {
+ safe_strncpy(buf, h_ent->h_name, buflen);
+ return buf;
+ }
}
}
-#endif
- return rt_addr_n2a(af, len, addr, buf, buflen);
+ return rt_addr_n2a(af, addr, buf, buflen);
}
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/utils.h b/release/src/router/busybox/networking/libiproute/utils.h
index dc28c1b6..ed03e785 100644
--- a/release/src/router/busybox/networking/libiproute/utils.h
+++ b/release/src/router/busybox/networking/libiproute/utils.h
@@ -1,20 +1,20 @@
-#ifndef __UTILS_H__
-#define __UTILS_H__ 1
-
-#include <asm/types.h>
-#include <resolv.h>
+/* vi: set sw=4 ts=4: */
+#ifndef UTILS_H
+#define UTILS_H 1
#include "libnetlink.h"
#include "ll_map.h"
#include "rtm_map.h"
-extern int preferred_family;
-extern int show_stats;
-extern int show_details;
-extern int show_raw;
-extern int resolve_hosts;
-extern int oneline;
-extern char * _SL_;
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+extern family_t preferred_family;
+extern smallint show_stats; /* UNUSED */
+extern smallint show_details; /* UNUSED */
+extern smallint show_raw; /* UNUSED */
+extern smallint resolve_hosts; /* UNUSED */
+extern smallint oneline;
+extern char _SL_;
#ifndef IPPROTO_ESP
#define IPPROTO_ESP 50
@@ -26,61 +26,58 @@ extern char * _SL_;
#define SPRINT_BSIZE 64
#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
-extern void incomplete_command(void) __attribute__((noreturn));
+extern void incomplete_command(void) NORETURN;
-#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
+#define NEXT_ARG() do { if (!*++argv) incomplete_command(); } while (0)
-typedef struct
-{
- __u8 family;
- __u8 bytelen;
- __s16 bitlen;
- __u32 data[4];
+typedef struct {
+ uint8_t family;
+ uint8_t bytelen;
+ int16_t bitlen;
+ uint32_t data[4];
} inet_prefix;
+#define PREFIXLEN_SPECIFIED 1
+
#define DN_MAXADDL 20
#ifndef AF_DECnet
#define AF_DECnet 12
#endif
-struct dn_naddr
-{
- unsigned short a_len;
- unsigned char a_addr[DN_MAXADDL];
+struct dn_naddr {
+ unsigned short a_len;
+ unsigned char a_addr[DN_MAXADDL];
};
#define IPX_NODE_LEN 6
struct ipx_addr {
- u_int32_t ipx_net;
- u_int8_t ipx_node[IPX_NODE_LEN];
+ uint32_t ipx_net;
+ uint8_t ipx_node[IPX_NODE_LEN];
};
-extern __u32 get_addr32(char *name);
+extern uint32_t get_addr32(char *name);
extern int get_addr_1(inet_prefix *dst, char *arg, int family);
-extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
+/*extern int get_prefix_1(inet_prefix *dst, char *arg, int family);*/
extern int get_addr(inet_prefix *dst, char *arg, int family);
extern int get_prefix(inet_prefix *dst, char *arg, int family);
-extern int get_integer(int *val, char *arg, int base);
-extern int get_unsigned(unsigned *val, char *arg, int base);
-#define get_byte get_u8
-#define get_ushort get_u16
-#define get_short get_s16
-extern int get_u32(__u32 *val, char *arg, int base);
-extern int get_u16(__u16 *val, char *arg, int base);
-extern int get_s16(__s16 *val, char *arg, int base);
-extern int get_u8(__u8 *val, char *arg, int base);
-extern int get_s8(__s8 *val, char *arg, int base);
+extern unsigned get_unsigned(char *arg, const char *errmsg);
+extern uint32_t get_u32(char *arg, const char *errmsg);
+extern uint16_t get_u16(char *arg, const char *errmsg);
+extern const char *rt_addr_n2a(int af, void *addr, char *buf, int buflen);
+#ifdef RESOLVE_HOSTNAMES
extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
-extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
+#else
+#define format_host(af, len, addr, buf, buflen) \
+ rt_addr_n2a(af, addr, buf, buflen)
+#endif
-void invarg(char *, char *) __attribute__((noreturn));
-void duparg(char *, char *) __attribute__((noreturn));
-void duparg2(char *, char *) __attribute__((noreturn));
-int matches(char *arg, char *pattern);
-extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+void invarg(const char *, const char *) NORETURN;
+void duparg(const char *, const char *) NORETURN;
+void duparg2(const char *, const char *) NORETURN;
+int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
int dnet_pton(int af, const char *src, void *addr);
@@ -88,14 +85,6 @@ int dnet_pton(int af, const char *src, void *addr);
const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
int ipx_pton(int af, const char *src, void *addr);
-extern int __iproute2_hz_internal;
-extern int __get_hz(void);
-
-static __inline__ int get_hz(void)
-{
- if (__iproute2_hz_internal == 0)
- __iproute2_hz_internal = __get_hz();
- return __iproute2_hz_internal;
-}
+POP_SAVED_FUNCTION_VISIBILITY
-#endif /* __UTILS_H__ */
+#endif
diff --git a/release/src/router/busybox/networking/nameif.c b/release/src/router/busybox/networking/nameif.c
index dca7c8c0..fb31fbff 100644
--- a/release/src/router/busybox/networking/nameif.c
+++ b/release/src/router/busybox/networking/nameif.c
@@ -1,222 +1,234 @@
+/* vi: set sw=4 ts=4: */
/*
* nameif.c - Naming Interfaces based on MAC address for busybox.
*
- * Writen 2000 by Andi Kleen.
+ * Written 2000 by Andi Kleen.
* Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
- * Glenn McGrath <bug1@optushome.com.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
+ * Glenn McGrath
+ * Extended matching support 2008 by Nico Erfurth <masta@perlgolf.de>
*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-
-#include <sys/syslog.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
+#include "libbb.h"
+#include <syslog.h>
#include <net/if.h>
#include <netinet/ether.h>
+#include <linux/sockios.h>
-#include "busybox.h"
-
-/* Older versions of net/if.h do not appear to define IF_NAMESIZE. */
-#ifndef IF_NAMESIZE
-# ifdef IFNAMSIZ
-# define IF_NAMESIZE IFNAMSIZ
-# else
-# define IF_NAMESIZE 16
-# endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
#endif
-/* take from linux/sockios.h */
+/* Taken from linux/sockios.h */
#define SIOCSIFNAME 0x8923 /* set interface name */
-/* Octets in one ethernet addr, from <linux/if_ether.h> */
+/* Octets in one Ethernet addr, from <linux/if_ether.h> */
#define ETH_ALEN 6
#ifndef ifr_newname
#define ifr_newname ifr_ifru.ifru_slave
#endif
-typedef struct mactable_s {
- struct mactable_s *next;
- struct mactable_s *prev;
+typedef struct ethtable_s {
+ struct ethtable_s *next;
+ struct ethtable_s *prev;
char *ifname;
struct ether_addr *mac;
-} mactable_t;
-
-static unsigned char use_syslog;
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ char *bus_info;
+ char *driver;
+#endif
+} ethtable_t;
+
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+/* Cut'n'paste from ethtool.h */
+#define ETHTOOL_BUSINFO_LEN 32
+/* these strings are set to whatever the driver author decides... */
+struct ethtool_drvinfo {
+ uint32_t cmd;
+ char driver[32]; /* driver short name, "tulip", "eepro100" */
+ char version[32]; /* driver version string */
+ char fw_version[32]; /* firmware version string, if applicable */
+ char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */
+ /* For PCI devices, use pci_dev->slot_name. */
+ char reserved1[32];
+ char reserved2[16];
+ uint32_t n_stats; /* number of u64's from ETHTOOL_GSTATS */
+ uint32_t testinfo_len;
+ uint32_t eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */
+ uint32_t regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */
+};
+#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */
+#endif
-static void serror(const char *s, ...) __attribute__ ((noreturn));
-static void serror(const char *s, ...)
+static void nameif_parse_selector(ethtable_t *ch, char *selector)
{
- va_list ap;
-
- va_start(ap, s);
+ struct ether_addr *lmac;
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ int found_selector = 0;
- if (use_syslog) {
- openlog(bb_applet_name, 0, LOG_LOCAL0);
- vsyslog(LOG_ERR, s, ap);
- closelog();
- } else {
- bb_verror_msg(s, ap);
- putc('\n', stderr);
+ while (*selector) {
+ char *next;
+#endif
+ selector = skip_whitespace(selector);
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ if (*selector == '\0')
+ break;
+ /* Search for the end .... */
+ next = skip_non_whitespace(selector);
+ if (*next)
+ *next++ = '\0';
+ /* Check for selectors, mac= is assumed */
+ if (strncmp(selector, "bus=", 4) == 0) {
+ ch->bus_info = xstrdup(selector + 4);
+ found_selector++;
+ } else if (strncmp(selector, "driver=", 7) == 0) {
+ ch->driver = xstrdup(selector + 7);
+ found_selector++;
+ } else {
+#endif
+ lmac = xmalloc(ETH_ALEN);
+ ch->mac = ether_aton_r(selector + (strncmp(selector, "mac=", 4) ? 0 : 4), lmac);
+ if (ch->mac == NULL)
+ bb_error_msg_and_die("cannot parse %s", selector);
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ found_selector++;
+ };
+ selector = next;
}
-
- va_end(ap);
-
- exit(EXIT_FAILURE);
+ if (found_selector == 0)
+ bb_error_msg_and_die("no selectors found for %s", ch->ifname);
+#endif
}
-/* Check ascii str_macaddr, convert and copy to *mac */
-struct ether_addr *cc_macaddr(char *str_macaddr)
+static void prepend_new_eth_table(ethtable_t **clist, char *ifname, char *selector)
{
- struct ether_addr *lmac, *mac;
-
- lmac = ether_aton(str_macaddr);
- if (lmac == NULL)
- serror("cannot parse MAC %s", str_macaddr);
- mac = xmalloc(ETH_ALEN);
- memcpy(mac, lmac, ETH_ALEN);
-
- return mac;
+ ethtable_t *ch;
+ if (strlen(ifname) >= IFNAMSIZ)
+ bb_error_msg_and_die("interface name '%s' too long", ifname);
+ ch = xzalloc(sizeof(*ch));
+ ch->ifname = xstrdup(ifname);
+ nameif_parse_selector(ch, selector);
+ ch->next = *clist;
+ if (*clist)
+ (*clist)->prev = ch;
+ *clist = ch;
}
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_eth_table(ethtable_t *ch)
+{
+ free(ch->ifname);
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ free(ch->bus_info);
+ free(ch->driver);
+#endif
+ free(ch->mac);
+ free(ch);
+};
+#else
+void delete_eth_table(ethtable_t *ch);
+#endif
+
+int nameif_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int nameif_main(int argc, char **argv)
{
- mactable_t *clist = NULL;
- FILE *ifh;
+ ethtable_t *clist = NULL;
const char *fname = "/etc/mactab";
- char *line;
int ctl_sk;
- int opt;
- int if_index = 1;
- mactable_t *ch;
-
-
- while ((opt = getopt(argc, argv, "c:s")) != -1) {
- switch (opt) {
- case 'c':
- fname = optarg;
- break;
- case 's':
- use_syslog = 1;
- break;
- default:
- bb_show_usage();
- }
+ ethtable_t *ch;
+ parser_t *parser;
+ char *token[2];
+
+ if (1 & getopt32(argv, "sc:", &fname)) {
+ openlog(applet_name, 0, LOG_LOCAL0);
+ /* Why not just "="? I assume logging to stderr
+ * can't hurt. 2>/dev/null if you don't like it: */
+ logmode |= LOGMODE_SYSLOG;
}
+ argc -= optind;
+ argv += optind;
- if ((argc - optind) & 1)
+ if (argc & 1)
bb_show_usage();
- if (optind < argc) {
- char **a = argv + optind;
-
- while (*a) {
-
- if (strlen(*a) > IF_NAMESIZE)
- serror("interface name `%s' too long", *a);
- ch = xcalloc(1, sizeof(mactable_t));
- ch->ifname = bb_xstrdup(*a++);
- ch->mac = cc_macaddr(*a++);
- if (clist)
- clist->prev = ch;
- ch->next = clist;
- clist = ch;
+ if (argc) {
+ while (*argv) {
+ char *ifname = xstrdup(*argv++);
+ prepend_new_eth_table(&clist, ifname, *argv++);
}
} else {
- ifh = bb_xfopen(fname, "r");
-
- while ((line = bb_get_line_from_file(ifh)) != NULL) {
- char *line_ptr;
- size_t name_length;
-
- line_ptr = line + strspn(line, " \t");
- if ((line_ptr[0] == '#') || (line_ptr[0] == '\n'))
- continue;
- name_length = strcspn(line_ptr, " \t");
- ch = xcalloc(1, sizeof(mactable_t));
- ch->ifname = bb_xstrndup(line_ptr, name_length);
- if (name_length > IF_NAMESIZE)
- serror("interface name `%s' too long", ch->ifname);
- line_ptr += name_length;
- line_ptr += strspn(line_ptr, " \t");
- name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:");
- line_ptr[name_length] = '\0';
- ch->mac = cc_macaddr(line_ptr);
- if (clist)
- clist->prev = ch;
- ch->next = clist;
- clist = ch;
- free(line);
- }
- fclose(ifh);
+ parser = config_open(fname);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL))
+ prepend_new_eth_table(&clist, token[0], token[1]);
+ config_close(parser);
}
- if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
- serror("socket: %m");
+ ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0);
+ parser = config_open2("/proc/net/dev", xfopen_for_read);
- while (clist) {
+ while (clist && config_read(parser, token, 2, 2, "\0: \t", PARSE_NORMAL)) {
struct ifreq ifr;
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ struct ethtool_drvinfo drvinfo;
+#endif
+ if (parser->lineno < 2)
+ continue; /* Skip the first two lines */
+
+ /* Find the current interface name and copy it to ifr.ifr_name */
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strncpy_IFNAMSIZ(ifr.ifr_name, token[0]);
+
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ /* Check for driver etc. */
+ memset(&drvinfo, 0, sizeof(struct ethtool_drvinfo));
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ ifr.ifr_data = (caddr_t) &drvinfo;
+ /* Get driver and businfo first, so we have it in drvinfo */
+ ioctl(ctl_sk, SIOCETHTOOL, &ifr);
+#endif
+ ioctl(ctl_sk, SIOCGIFHWADDR, &ifr);
- bzero(&ifr, sizeof(struct ifreq));
- if_index++;
- ifr.ifr_ifindex = if_index;
-
- /* Get ifname by index or die */
- if (ioctl(ctl_sk, SIOCGIFNAME, &ifr))
+ /* Search the list for a matching device */
+ for (ch = clist; ch; ch = ch->next) {
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+ if (ch->bus_info && strcmp(ch->bus_info, drvinfo.bus_info) != 0)
+ continue;
+ if (ch->driver && strcmp(ch->driver, drvinfo.driver) != 0)
+ continue;
+#endif
+ if (ch->mac && memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN) != 0)
+ continue;
+ /* if we came here, all selectors have matched */
break;
-
- /* Has this device hwaddr? */
- if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr))
- continue;
-
- /* Search for mac like in ifr.ifr_hwaddr.sa_data */
- for (ch = clist; ch; ch = ch->next)
- if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN))
- break;
-
- /* Nothing found for current ifr.ifr_hwaddr.sa_data */
- if (ch == NULL)
+ }
+ /* Nothing found for current interface */
+ if (!ch)
continue;
- strcpy(ifr.ifr_newname, ch->ifname);
- if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0)
- serror("cannot change ifname %s to %s: %m",
- ifr.ifr_name, ch->ifname);
-
+ if (strcmp(ifr.ifr_name, ch->ifname) != 0) {
+ strcpy(ifr.ifr_newname, ch->ifname);
+ ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr,
+ "cannot change ifname %s to %s",
+ ifr.ifr_name, ch->ifname);
+ }
/* Remove list entry of renamed interface */
- if (ch->prev != NULL) {
- (ch->prev)->next = ch->next;
- } else {
+ if (ch->prev != NULL)
+ ch->prev->next = ch->next;
+ else
clist = ch->next;
- }
if (ch->next != NULL)
- (ch->next)->prev = ch->prev;
-#ifdef CONFIG_FEATURE_CLEAN_UP
- free(ch->ifname);
- free(ch->mac);
- free(ch);
-#endif
+ ch->next->prev = ch->prev;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ delete_eth_table(ch);
}
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ for (ch = clist; ch; ch = ch->next)
+ delete_eth_table(ch);
+ config_close(parser);
+ };
return 0;
}
diff --git a/release/src/router/busybox/networking/nc.c b/release/src/router/busybox/networking/nc.c
index 4888ccce..fe845f58 100644
--- a/release/src/router/busybox/networking/nc.c
+++ b/release/src/router/busybox/networking/nc.c
@@ -1,147 +1,171 @@
/* vi: set sw=4 ts=4: */
/* nc: mini-netcat - built from the ground up for LRP
- Copyright (C) 1998 Charles P. Wright
+ *
+ * Copyright (C) 1998, 1999 Charles P. Wright
+ * Copyright (C) 1998 Dave Cinege
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
- 0.0.1 6K It works.
- 0.0.2 5K Smaller and you can also check the exit condition if you wish.
- 0.0.3 Uses select()
+#include "libbb.h"
- 19980918 Busy Boxed! Dave Cinege
- 19990512 Uses Select. Charles P. Wright
- 19990513 Fixes stdin stupidity and uses buffers. Charles P. Wright
+#if ENABLE_DESKTOP
+#include "nc_bloaty.c"
+#else
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+/* Lots of small differences in features
+ * when compared to "standard" nc
+ */
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/time.h>
-#include <sys/ioctl.h>
-#include "busybox.h"
-
-#define GAPING_SECURITY_HOLE
+static void timeout(int signum UNUSED_PARAM)
+{
+ bb_error_msg_and_die("timed out");
+}
+int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int nc_main(int argc, char **argv)
{
- int do_listen = 0, lport = 0, delay = 0, tmpfd, opt, sfd, x;
- char buf[BUFSIZ];
-#ifdef GAPING_SECURITY_HOLE
- char * pr00gie = NULL;
-#endif
-
- struct sockaddr_in address;
- struct hostent *hostinfo;
-
+ /* sfd sits _here_ only because of "repeat" option (-l -l). */
+ int sfd = sfd; /* for gcc */
+ int cfd = 0;
+ unsigned lport = 0;
+ SKIP_NC_SERVER(const) unsigned do_listen = 0;
+ SKIP_NC_EXTRA (const) unsigned wsecs = 0;
+ SKIP_NC_EXTRA (const) unsigned delay = 0;
+ SKIP_NC_EXTRA (const int execparam = 0;)
+ USE_NC_EXTRA (char **execparam = NULL;)
+ len_and_sockaddr *lsa;
fd_set readfds, testfds;
-
- while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) {
- switch (opt) {
- case 'l':
- do_listen++;
- break;
- case 'p':
- lport = atoi(optarg);
- break;
- case 'i':
- delay = atoi(optarg);
- break;
-#ifdef GAPING_SECURITY_HOLE
- case 'e':
- pr00gie = optarg;
- break;
-#endif
- default:
- bb_show_usage();
+ int opt; /* must be signed (getopt returns -1) */
+
+ if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
+ /* getopt32 is _almost_ usable:
+ ** it cannot handle "... -e prog -prog-opt" */
+ while ((opt = getopt(argc, argv,
+ "" USE_NC_SERVER("lp:") USE_NC_EXTRA("w:i:f:e:") )) > 0
+ ) {
+ if (ENABLE_NC_SERVER && opt=='l')
+ USE_NC_SERVER(do_listen++);
+ else if (ENABLE_NC_SERVER && opt=='p')
+ USE_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0));
+ else if (ENABLE_NC_EXTRA && opt=='w')
+ USE_NC_EXTRA( wsecs = xatou(optarg));
+ else if (ENABLE_NC_EXTRA && opt=='i')
+ USE_NC_EXTRA( delay = xatou(optarg));
+ else if (ENABLE_NC_EXTRA && opt=='f')
+ USE_NC_EXTRA( cfd = xopen(optarg, O_RDWR));
+ else if (ENABLE_NC_EXTRA && opt=='e' && optind <= argc) {
+ /* We cannot just 'break'. We should let getopt finish.
+ ** Or else we won't be able to find where
+ ** 'host' and 'port' params are
+ ** (think "nc -w 60 host port -e prog"). */
+ USE_NC_EXTRA(
+ char **p;
+ // +2: one for progname (optarg) and one for NULL
+ execparam = xzalloc(sizeof(char*) * (argc - optind + 2));
+ p = execparam;
+ *p++ = optarg;
+ while (optind < argc) {
+ *p++ = argv[optind++];
+ }
+ )
+ /* optind points to argv[arvc] (NULL) now.
+ ** FIXME: we assume that getopt will not count options
+ ** possibly present on "-e prog args" and will not
+ ** include them into final value of optind
+ ** which is to be used ... */
+ } else bb_show_usage();
}
+ argv += optind; /* ... here! */
+ argc -= optind;
+ // -l and -f don't mix
+ if (do_listen && cfd) bb_show_usage();
+ // Listen or file modes need zero arguments, client mode needs 2
+ if (do_listen || cfd) {
+ if (argc) bb_show_usage();
+ } else {
+ if (!argc || argc > 2) bb_show_usage();
+ }
+ } else {
+ if (argc != 3) bb_show_usage();
+ argc--;
+ argv++;
}
-#ifdef GAPING_SECURITY_HOLE
- if (pr00gie) {
- /* won't need stdin */
- close (fileno(stdin));
+ if (wsecs) {
+ signal(SIGALRM, timeout);
+ alarm(wsecs);
}
-#endif /* GAPING_SECURITY_HOLE */
-
-
- if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc))
- bb_show_usage();
- if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- bb_perror_msg_and_die("socket");
- x = 1;
- if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
- bb_perror_msg_and_die ("reuseaddr failed");
- address.sin_family = AF_INET;
-
- if (lport != 0) {
- memset(&address.sin_addr, 0, sizeof(address.sin_addr));
- address.sin_port = htons(lport);
-
- if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
- bb_perror_msg_and_die("bind");
+ if (!cfd) {
+ if (do_listen) {
+ /* create_and_bind_stream_or_die(NULL, lport)
+ * would've work wonderfully, but we need
+ * to know lsa */
+ sfd = xsocket_stream(&lsa);
+ if (lport)
+ set_nport(lsa, htons(lport));
+ setsockopt_reuseaddr(sfd);
+ xbind(sfd, &lsa->u.sa, lsa->len);
+ xlisten(sfd, do_listen); /* can be > 1 */
+ /* If we didn't specify a port number,
+ * query and print it after listen() */
+ if (!lport) {
+ socklen_t addrlen = lsa->len;
+ getsockname(sfd, &lsa->u.sa, &addrlen);
+ lport = get_nport(&lsa->u.sa);
+ fdprintf(2, "%d\n", ntohs(lport));
+ }
+ close_on_exec_on(sfd);
+ accept_again:
+ cfd = accept(sfd, NULL, 0);
+ if (cfd < 0)
+ bb_perror_msg_and_die("accept");
+ if (!execparam)
+ close(sfd);
+ } else {
+ cfd = create_and_connect_stream_or_die(argv[0],
+ argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0);
+ }
}
- if (do_listen) {
- socklen_t addrlen = sizeof(address);
-
- if (listen(sfd, 1) < 0)
- bb_perror_msg_and_die("listen");
-
- if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0)
- bb_perror_msg_and_die("accept");
-
- close(sfd);
- sfd = tmpfd;
- } else {
- hostinfo = xgethostbyname(argv[optind]);
-
- address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
- address.sin_port = htons(atoi(argv[optind+1]));
-
- if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
- bb_perror_msg_and_die("connect");
+ if (wsecs) {
+ alarm(0);
+ /* Non-ignored siganls revert to SIG_DFL on exec anyway */
+ /*signal(SIGALRM, SIG_DFL);*/
}
-#ifdef GAPING_SECURITY_HOLE
/* -e given? */
- if (pr00gie) {
- dup2(sfd, 0);
- close(sfd);
- dup2 (0, 1);
- dup2 (0, 2);
- execl (pr00gie, pr00gie, NULL);
+ if (execparam) {
+ signal(SIGCHLD, SIG_IGN);
+ // With more than one -l, repeatedly act as server.
+ if (do_listen > 1 && vfork()) {
+ /* parent */
+ // This is a bit weird as cleanup goes, since we wind up with no
+ // stdin/stdout/stderr. But it's small and shouldn't hurt anything.
+ // We check for cfd == 0 above.
+ logmode = LOGMODE_NONE;
+ close(0);
+ close(1);
+ close(2);
+ goto accept_again;
+ }
+ /* child (or main thread if no multiple -l) */
+ xmove_fd(cfd, 0);
+ xdup2(0, 1);
+ xdup2(0, 2);
+ USE_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
/* Don't print stuff or it will go over the wire.... */
- _exit(-1);
+ _exit(127);
}
-#endif /* GAPING_SECURITY_HOLE */
+ // Select loop copying stdin to cfd, and cfd to stdout.
FD_ZERO(&readfds);
- FD_SET(sfd, &readfds);
+ FD_SET(cfd, &readfds);
FD_SET(STDIN_FILENO, &readfds);
- while (1) {
+ for (;;) {
int fd;
int ofd;
int nread;
@@ -151,27 +175,27 @@ int nc_main(int argc, char **argv)
if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
bb_perror_msg_and_die("select");
+#define iobuf bb_common_bufsiz1
for (fd = 0; fd < FD_SETSIZE; fd++) {
if (FD_ISSET(fd, &testfds)) {
- if ((nread = safe_read(fd, buf, sizeof(buf))) < 0)
- bb_perror_msg_and_die("read");
-
- if (fd == sfd) {
- if (nread == 0)
- exit(0);
+ nread = safe_read(fd, iobuf, sizeof(iobuf));
+ if (fd == cfd) {
+ if (nread < 1)
+ exit(EXIT_SUCCESS);
ofd = STDOUT_FILENO;
} else {
- if (nread == 0)
- shutdown(sfd, 1);
- ofd = sfd;
- }
-
- if (bb_full_write(ofd, buf, nread) < 0)
- bb_perror_msg_and_die("write");
- if (delay > 0) {
- sleep(delay);
+ if (nread<1) {
+ // Close outgoing half-connection so they get EOF, but
+ // leave incoming alone so we can see response.
+ shutdown(cfd, 1);
+ FD_CLR(STDIN_FILENO, &readfds);
+ }
+ ofd = cfd;
}
+ xwrite(ofd, iobuf, nread);
+ if (delay > 0) sleep(delay);
}
}
}
}
+#endif
diff --git a/release/src/router/busybox/networking/nc_bloaty.c b/release/src/router/busybox/networking/nc_bloaty.c
new file mode 100644
index 00000000..41db9452
--- /dev/null
+++ b/release/src/router/busybox/networking/nc_bloaty.c
@@ -0,0 +1,832 @@
+/* Based on netcat 1.10 RELEASE 960320 written by hobbit@avian.org.
+ * Released into public domain by the author.
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+/* Author's comments from nc 1.10:
+ * =====================
+ * Netcat is entirely my own creation, although plenty of other code was used as
+ * examples. It is freely given away to the Internet community in the hope that
+ * it will be useful, with no restrictions except giving credit where it is due.
+ * No GPLs, Berkeley copyrights or any of that nonsense. The author assumes NO
+ * responsibility for how anyone uses it. If netcat makes you rich somehow and
+ * you're feeling generous, mail me a check. If you are affiliated in any way
+ * with Microsoft Network, get a life. Always ski in control. Comments,
+ * questions, and patches to hobbit@avian.org.
+ * ...
+ * Netcat and the associated package is a product of Avian Research, and is freely
+ * available in full source form with no restrictions save an obligation to give
+ * credit where due.
+ * ...
+ * A damn useful little "backend" utility begun 950915 or thereabouts,
+ * as *Hobbit*'s first real stab at some sockets programming. Something that
+ * should have and indeed may have existed ten years ago, but never became a
+ * standard Unix utility. IMHO, "nc" could take its place right next to cat,
+ * cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
+ * =====================
+ *
+ * Much of author's comments are still retained in the code.
+ *
+ * Functionality removed (rationale):
+ * - miltiple-port ranges, randomized port scanning (use nmap)
+ * - telnet support (use telnet)
+ * - source routing
+ * - multiple DNS checks
+ * Functionalty which is different from nc 1.10:
+ * - Prog in '-e prog' can have prog's parameters and options.
+ * Because of this -e option must be last.
+ * - nc doesn't redirect stderr to the network socket for the -e prog.
+ * - numeric addresses are printed in (), not [] (IPv6 looks better),
+ * port numbers are inside (): (1.2.3.4:5678)
+ * - network read errors are reported on verbose levels > 1
+ * (nc 1.10 treats them as EOF)
+ * - TCP connects from wrong ip/ports (if peer ip:port is specified
+ * on the command line, but accept() says that it came from different addr)
+ * are closed, but nc doesn't exit - continues to listen/accept.
+ */
+
+/* done in nc.c: #include "libbb.h" */
+
+enum {
+ SLEAZE_PORT = 31337, /* for UDP-scan RTT trick, change if ya want */
+ BIGSIZ = 8192, /* big buffers */
+
+ netfd = 3,
+ ofd = 4,
+};
+
+struct globals {
+ /* global cmd flags: */
+ unsigned o_verbose;
+ unsigned o_wait;
+#if ENABLE_NC_EXTRA
+ unsigned o_interval;
+#endif
+
+ /*int netfd;*/
+ /*int ofd;*/ /* hexdump output fd */
+#if ENABLE_LFS
+#define SENT_N_RECV_M "sent %llu, rcvd %llu\n"
+ unsigned long long wrote_out; /* total stdout bytes */
+ unsigned long long wrote_net; /* total net bytes */
+#else
+#define SENT_N_RECV_M "sent %u, rcvd %u\n"
+ unsigned wrote_out; /* total stdout bytes */
+ unsigned wrote_net; /* total net bytes */
+#endif
+ /* ouraddr is never NULL and goes through three states as we progress:
+ 1 - local address before bind (IP/port possibly zero)
+ 2 - local address after bind (port is nonzero)
+ 3 - local address after connect??/recv/accept (IP and port are nonzero) */
+ struct len_and_sockaddr *ouraddr;
+ /* themaddr is NULL if no peer hostname[:port] specified on command line */
+ struct len_and_sockaddr *themaddr;
+ /* remend is set after connect/recv/accept to the actual ip:port of peer */
+ struct len_and_sockaddr remend;
+
+ jmp_buf jbuf; /* timer crud */
+
+ /* will malloc up the following globals: */
+ fd_set ding1; /* for select loop */
+ fd_set ding2;
+ char bigbuf_in[BIGSIZ]; /* data buffers */
+ char bigbuf_net[BIGSIZ];
+};
+
+#define G (*ptr_to_globals)
+#define wrote_out (G.wrote_out )
+#define wrote_net (G.wrote_net )
+#define ouraddr (G.ouraddr )
+#define themaddr (G.themaddr )
+#define remend (G.remend )
+#define jbuf (G.jbuf )
+#define ding1 (G.ding1 )
+#define ding2 (G.ding2 )
+#define bigbuf_in (G.bigbuf_in )
+#define bigbuf_net (G.bigbuf_net)
+#define o_verbose (G.o_verbose )
+#define o_wait (G.o_wait )
+#if ENABLE_NC_EXTRA
+#define o_interval (G.o_interval)
+#else
+#define o_interval 0
+#endif
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/* Must match getopt32 call! */
+enum {
+ OPT_h = (1 << 0),
+ OPT_n = (1 << 1),
+ OPT_p = (1 << 2),
+ OPT_s = (1 << 3),
+ OPT_u = (1 << 4),
+ OPT_v = (1 << 5),
+ OPT_w = (1 << 6),
+ OPT_l = (1 << 7) * ENABLE_NC_SERVER,
+ OPT_i = (1 << (7+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
+ OPT_o = (1 << (8+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
+ OPT_z = (1 << (9+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
+};
+
+#define o_nflag (option_mask32 & OPT_n)
+#define o_udpmode (option_mask32 & OPT_u)
+#if ENABLE_NC_SERVER
+#define o_listen (option_mask32 & OPT_l)
+#else
+#define o_listen 0
+#endif
+#if ENABLE_NC_EXTRA
+#define o_ofile (option_mask32 & OPT_o)
+#define o_zero (option_mask32 & OPT_z)
+#else
+#define o_ofile 0
+#define o_zero 0
+#endif
+
+/* Debug: squirt whatever message and sleep a bit so we can see it go by. */
+/* Beware: writes to stdOUT... */
+#if 0
+#define Debug(...) do { printf(__VA_ARGS__); printf("\n"); fflush(stdout); sleep(1); } while (0)
+#else
+#define Debug(...) do { } while (0)
+#endif
+
+#define holler_error(...) do { if (o_verbose) bb_error_msg(__VA_ARGS__); } while (0)
+#define holler_perror(...) do { if (o_verbose) bb_perror_msg(__VA_ARGS__); } while (0)
+
+/* catch: no-brainer interrupt handler */
+static void catch(int sig)
+{
+ if (o_verbose > 1) /* normally we don't care */
+ fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
+ fprintf(stderr, "punt!\n");
+ kill_myself_with_sig(sig);
+}
+
+/* unarm */
+static void unarm(void)
+{
+ signal(SIGALRM, SIG_IGN);
+ alarm(0);
+}
+
+/* timeout and other signal handling cruft */
+static void tmtravel(int sig UNUSED_PARAM)
+{
+ unarm();
+ longjmp(jbuf, 1);
+}
+
+/* arm: set the timer. */
+static void arm(unsigned secs)
+{
+ signal(SIGALRM, tmtravel);
+ alarm(secs);
+}
+
+/* findline:
+ find the next newline in a buffer; return inclusive size of that "line",
+ or the entire buffer size, so the caller knows how much to then write().
+ Not distinguishing \n vs \r\n for the nonce; it just works as is... */
+static unsigned findline(char *buf, unsigned siz)
+{
+ char * p;
+ int x;
+ if (!buf) /* various sanity checks... */
+ return 0;
+ if (siz > BIGSIZ)
+ return 0;
+ x = siz;
+ for (p = buf; x > 0; x--) {
+ if (*p == '\n') {
+ x = (int) (p - buf);
+ x++; /* 'sokay if it points just past the end! */
+Debug("findline returning %d", x);
+ return x;
+ }
+ p++;
+ } /* for */
+Debug("findline returning whole thing: %d", siz);
+ return siz;
+} /* findline */
+
+/* doexec:
+ fiddle all the file descriptors around, and hand off to another prog. Sort
+ of like a one-off "poor man's inetd". This is the only section of code
+ that would be security-critical, which is why it's ifdefed out by default.
+ Use at your own hairy risk; if you leave shells lying around behind open
+ listening ports you deserve to lose!! */
+static int doexec(char **proggie) NORETURN;
+static int doexec(char **proggie)
+{
+ xmove_fd(netfd, 0);
+ dup2(0, 1);
+ /* dup2(0, 2); - do we *really* want this? NO!
+ * exec'ed prog can do it yourself, if needed */
+ execvp(proggie[0], proggie);
+ bb_perror_msg_and_die("exec");
+}
+
+/* connect_w_timeout:
+ return an fd for one of
+ an open outbound TCP connection, a UDP stub-socket thingie, or
+ an unconnected TCP or UDP socket to listen on.
+ Examines various global o_blah flags to figure out what to do.
+ lad can be NULL, then socket is not bound to any local ip[:port] */
+static int connect_w_timeout(int fd)
+{
+ int rr;
+
+ /* wrap connect inside a timer, and hit it */
+ arm(o_wait);
+ if (setjmp(jbuf) == 0) {
+ rr = connect(fd, &themaddr->u.sa, themaddr->len);
+ unarm();
+ } else { /* setjmp: connect failed... */
+ rr = -1;
+ errno = ETIMEDOUT; /* fake it */
+ }
+ return rr;
+}
+
+/* dolisten:
+ listens for
+ incoming and returns an open connection *from* someplace. If we were
+ given host/port args, any connections from elsewhere are rejected. This
+ in conjunction with local-address binding should limit things nicely... */
+static void dolisten(void)
+{
+ int rr;
+
+ if (!o_udpmode)
+ xlisten(netfd, 1); /* TCP: gotta listen() before we can get */
+
+ /* Various things that follow temporarily trash bigbuf_net, which might contain
+ a copy of any recvfrom()ed packet, but we'll read() another copy later. */
+
+ /* I can't believe I have to do all this to get my own goddamn bound address
+ and port number. It should just get filled in during bind() or something.
+ All this is only useful if we didn't say -p for listening, since if we
+ said -p we *know* what port we're listening on. At any rate we won't bother
+ with it all unless we wanted to see it, although listening quietly on a
+ random unknown port is probably not very useful without "netstat". */
+ if (o_verbose) {
+ char *addr;
+ rr = getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
+ if (rr < 0)
+ bb_perror_msg_and_die("getsockname after bind");
+ addr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
+ fprintf(stderr, "listening on %s ...\n", addr);
+ free(addr);
+ }
+
+ if (o_udpmode) {
+ /* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
+ party's particulars all at once, listen() and accept() don't apply.
+ At least in the BSD universe, however, recvfrom/PEEK is enough to tell
+ us something came in, and we can set things up so straight read/write
+ actually does work after all. Yow. YMMV on strange platforms! */
+
+ /* I'm not completely clear on how this works -- BSD seems to make UDP
+ just magically work in a connect()ed context, but we'll undoubtedly run
+ into systems this deal doesn't work on. For now, we apparently have to
+ issue a connect() on our just-tickled socket so we can write() back.
+ Again, why the fuck doesn't it just get filled in and taken care of?!
+ This hack is anything but optimal. Basically, if you want your listener
+ to also be able to send data back, you need this connect() line, which
+ also has the side effect that now anything from a different source or even a
+ different port on the other end won't show up and will cause ICMP errors.
+ I guess that's what they meant by "connect".
+ Let's try to remember what the "U" is *really* for, eh? */
+
+ /* If peer address is specified, connect to it */
+ remend.len = LSA_SIZEOF_SA;
+ if (themaddr) {
+ remend = *themaddr;
+ xconnect(netfd, &themaddr->u.sa, themaddr->len);
+ }
+ /* peek first packet and remember peer addr */
+ arm(o_wait); /* might as well timeout this, too */
+ if (setjmp(jbuf) == 0) { /* do timeout for initial connect */
+ /* (*ouraddr) is prefilled with "default" address */
+ /* and here we block... */
+ rr = recv_from_to(netfd, NULL, 0, MSG_PEEK, /*was bigbuf_net, BIGSIZ*/
+ &remend.u.sa, &ouraddr->u.sa, ouraddr->len);
+ if (rr < 0)
+ bb_perror_msg_and_die("recvfrom");
+ unarm();
+ } else
+ bb_error_msg_and_die("timeout");
+/* Now we learned *to which IP* peer has connected, and we want to anchor
+our socket on it, so that our outbound packets will have correct local IP.
+Unfortunately, bind() on already bound socket will fail now (EINVAL):
+ xbind(netfd, &ouraddr->u.sa, ouraddr->len);
+Need to read the packet, save data, close this socket and
+create new one, and bind() it. TODO */
+ if (!themaddr)
+ xconnect(netfd, &remend.u.sa, ouraddr->len);
+ } else {
+ /* TCP */
+ arm(o_wait); /* wrap this in a timer, too; 0 = forever */
+ if (setjmp(jbuf) == 0) {
+ again:
+ remend.len = LSA_SIZEOF_SA;
+ rr = accept(netfd, &remend.u.sa, &remend.len);
+ if (rr < 0)
+ bb_perror_msg_and_die("accept");
+ if (themaddr && memcmp(&remend.u.sa, &themaddr->u.sa, remend.len) != 0) {
+ /* nc 1.10 bails out instead, and its error message
+ * is not suppressed by o_verbose */
+ if (o_verbose) {
+ char *remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
+ bb_error_msg("connect from wrong ip/port %s ignored", remaddr);
+ free(remaddr);
+ }
+ close(rr);
+ goto again;
+ }
+ unarm();
+ } else
+ bb_error_msg_and_die("timeout");
+ xmove_fd(rr, netfd); /* dump the old socket, here's our new one */
+ /* find out what address the connection was *to* on our end, in case we're
+ doing a listen-on-any on a multihomed machine. This allows one to
+ offer different services via different alias addresses, such as the
+ "virtual web site" hack. */
+ rr = getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
+ if (rr < 0)
+ bb_perror_msg_and_die("getsockname after accept");
+ }
+
+ if (o_verbose) {
+ char *lcladdr, *remaddr, *remhostname;
+
+#if ENABLE_NC_EXTRA && defined(IP_OPTIONS)
+ /* If we can, look for any IP options. Useful for testing the receiving end of
+ such things, and is a good exercise in dealing with it. We do this before
+ the connect message, to ensure that the connect msg is uniformly the LAST
+ thing to emerge after all the intervening crud. Doesn't work for UDP on
+ any machines I've tested, but feel free to surprise me. */
+ char optbuf[40];
+ socklen_t x = sizeof(optbuf);
+
+ rr = getsockopt(netfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
+ if (rr < 0)
+ bb_perror_msg("getsockopt failed");
+ else if (x) { /* we've got options, lessee em... */
+ bin2hex(bigbuf_net, optbuf, x);
+ bigbuf_net[2*x] = '\0';
+ fprintf(stderr, "IP options: %s\n", bigbuf_net);
+ }
+#endif
+
+ /* now check out who it is. We don't care about mismatched DNS names here,
+ but any ADDR and PORT we specified had better fucking well match the caller.
+ Converting from addr to inet_ntoa and back again is a bit of a kludge, but
+ gethostpoop wants a string and there's much gnarlier code out there already,
+ so I don't feel bad.
+ The *real* question is why BFD sockets wasn't designed to allow listens for
+ connections *from* specific hosts/ports, instead of requiring the caller to
+ accept the connection and then reject undesireable ones by closing.
+ In other words, we need a TCP MSG_PEEK. */
+ /* bbox: removed most of it */
+ lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
+ remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
+ remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa);
+ fprintf(stderr, "connect to %s from %s (%s)\n",
+ lcladdr, remhostname, remaddr);
+ free(lcladdr);
+ free(remaddr);
+ if (!o_nflag)
+ free(remhostname);
+ }
+}
+
+/* udptest:
+ fire a couple of packets at a UDP target port, just to see if it's really
+ there. On BSD kernels, ICMP host/port-unreachable errors get delivered to
+ our socket as ECONNREFUSED write errors. On SV kernels, we lose; we'll have
+ to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
+ backend. Guess where one could swipe the appropriate code from...
+
+ Use the time delay between writes if given, otherwise use the "tcp ping"
+ trick for getting the RTT. [I got that idea from pluvius, and warped it.]
+ Return either the original fd, or clean up and return -1. */
+#if ENABLE_NC_EXTRA
+static int udptest(void)
+{
+ int rr;
+
+ rr = write(netfd, bigbuf_in, 1);
+ if (rr != 1)
+ bb_perror_msg("udptest first write");
+
+ if (o_wait)
+ sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)?
+ else {
+ /* use the tcp-ping trick: try connecting to a normally refused port, which
+ causes us to block for the time that SYN gets there and RST gets back.
+ Not completely reliable, but it *does* mostly work. */
+ /* Set a temporary connect timeout, so packet filtration doesnt cause
+ us to hang forever, and hit it */
+ o_wait = 5; /* enough that we'll notice?? */
+ rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0);
+ set_nport(themaddr, htons(SLEAZE_PORT));
+ connect_w_timeout(rr);
+ /* don't need to restore themaddr's port, it's not used anymore */
+ close(rr);
+ o_wait = 0; /* restore */
+ }
+
+ rr = write(netfd, bigbuf_in, 1);
+ return (rr != 1); /* if rr == 1, return 0 (success) */
+}
+#else
+int udptest(void);
+#endif
+
+/* oprint:
+ Hexdump bytes shoveled either way to a running logfile, in the format:
+ D offset - - - - --- 16 bytes --- - - - - # .... ascii .....
+ where "which" sets the direction indicator, D:
+ 0 -- sent to network, or ">"
+ 1 -- rcvd and printed to stdout, or "<"
+ and "buf" and "n" are data-block and length. If the current block generates
+ a partial line, so be it; we *want* that lockstep indication of who sent
+ what when. Adapted from dgaudet's original example -- but must be ripping
+ *fast*, since we don't want to be too disk-bound... */
+#if ENABLE_NC_EXTRA
+static void oprint(int direction, unsigned char *p, unsigned bc)
+{
+ unsigned obc; /* current "global" offset */
+ unsigned x;
+ unsigned char *op; /* out hexdump ptr */
+ unsigned char *ap; /* out asc-dump ptr */
+ unsigned char stage[100];
+
+ if (bc == 0)
+ return;
+
+ obc = wrote_net; /* use the globals! */
+ if (direction == '<')
+ obc = wrote_out;
+ stage[0] = direction;
+ stage[59] = '#'; /* preload separator */
+ stage[60] = ' ';
+
+ do { /* for chunk-o-data ... */
+ x = 16;
+ if (bc < 16) {
+ /* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */
+ memset(&stage[11], ' ', 16*3);
+ x = bc;
+ }
+ sprintf((char *)&stage[1], " %8.8x ", obc); /* xxx: still slow? */
+ bc -= x; /* fix current count */
+ obc += x; /* fix current offset */
+ op = &stage[11]; /* where hex starts */
+ ap = &stage[61]; /* where ascii starts */
+
+ do { /* for line of dump, however long ... */
+ *op++ = 0x20 | bb_hexdigits_upcase[*p >> 4];
+ *op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f];
+ *op++ = ' ';
+ if ((*p > 31) && (*p < 127))
+ *ap = *p; /* printing */
+ else
+ *ap = '.'; /* nonprinting, loose def */
+ ap++;
+ p++;
+ } while (--x);
+ *ap++ = '\n'; /* finish the line */
+ xwrite(ofd, stage, ap - stage);
+ } while (bc);
+}
+#else
+void oprint(int direction, unsigned char *p, unsigned bc);
+#endif
+
+/* readwrite:
+ handle stdin/stdout/network I/O. Bwahaha!! -- the select loop from hell.
+ In this instance, return what might become our exit status. */
+static int readwrite(void)
+{
+ int rr;
+ char *zp = zp; /* gcc */ /* stdin buf ptr */
+ char *np = np; /* net-in buf ptr */
+ unsigned rzleft;
+ unsigned rnleft;
+ unsigned netretry; /* net-read retry counter */
+ unsigned wretry; /* net-write sanity counter */
+ unsigned wfirst; /* one-shot flag to skip first net read */
+
+ /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
+ either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
+ FD_SET(netfd, &ding1); /* global: the net is open */
+ netretry = 2;
+ wfirst = 0;
+ rzleft = rnleft = 0;
+ if (o_interval)
+ sleep(o_interval); /* pause *before* sending stuff, too */
+
+ errno = 0; /* clear from sleep, close, whatever */
+ /* and now the big ol' select shoveling loop ... */
+ while (FD_ISSET(netfd, &ding1)) { /* i.e. till the *net* closes! */
+ wretry = 8200; /* more than we'll ever hafta write */
+ if (wfirst) { /* any saved stdin buffer? */
+ wfirst = 0; /* clear flag for the duration */
+ goto shovel; /* and go handle it first */
+ }
+ ding2 = ding1; /* FD_COPY ain't portable... */
+ /* some systems, notably linux, crap into their select timers on return, so
+ we create a expendable copy and give *that* to select. */
+ if (o_wait) {
+ struct timeval tmp_timer;
+ tmp_timer.tv_sec = o_wait;
+ tmp_timer.tv_usec = 0;
+ /* highest possible fd is netfd (3) */
+ rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer);
+ } else
+ rr = select(netfd+1, &ding2, NULL, NULL, NULL);
+ if (rr < 0 && errno != EINTR) { /* might have gotten ^Zed, etc */
+ holler_perror("select");
+ close(netfd);
+ return 1;
+ }
+ /* if we have a timeout AND stdin is closed AND we haven't heard anything
+ from the net during that time, assume it's dead and close it too. */
+ if (rr == 0) {
+ if (!FD_ISSET(STDIN_FILENO, &ding1))
+ netretry--; /* we actually try a coupla times. */
+ if (!netretry) {
+ if (o_verbose > 1) /* normally we don't care */
+ fprintf(stderr, "net timeout\n");
+ close(netfd);
+ return 0; /* not an error! */
+ }
+ } /* select timeout */
+ /* xxx: should we check the exception fds too? The read fds seem to give
+ us the right info, and none of the examples I found bothered. */
+
+ /* Ding!! Something arrived, go check all the incoming hoppers, net first */
+ if (FD_ISSET(netfd, &ding2)) { /* net: ding! */
+ rr = read(netfd, bigbuf_net, BIGSIZ);
+ if (rr <= 0) {
+ if (rr < 0 && o_verbose > 1) {
+ /* nc 1.10 doesn't do this */
+ bb_perror_msg("net read");
+ }
+ FD_CLR(netfd, &ding1); /* net closed, we'll finish up... */
+ rzleft = 0; /* can't write anymore: broken pipe */
+ } else {
+ rnleft = rr;
+ np = bigbuf_net;
+ }
+Debug("got %d from the net, errno %d", rr, errno);
+ } /* net:ding */
+
+ /* if we're in "slowly" mode there's probably still stuff in the stdin
+ buffer, so don't read unless we really need MORE INPUT! MORE INPUT! */
+ if (rzleft)
+ goto shovel;
+
+ /* okay, suck more stdin */
+ if (FD_ISSET(STDIN_FILENO, &ding2)) { /* stdin: ding! */
+ rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ);
+ /* Considered making reads here smaller for UDP mode, but 8192-byte
+ mobygrams are kinda fun and exercise the reassembler. */
+ if (rr <= 0) { /* at end, or fukt, or ... */
+ FD_CLR(STDIN_FILENO, &ding1); /* disable and close stdin */
+ close(0);
+ } else {
+ rzleft = rr;
+ zp = bigbuf_in;
+ }
+ } /* stdin:ding */
+ shovel:
+ /* now that we've dingdonged all our thingdings, send off the results.
+ Geez, why does this look an awful lot like the big loop in "rsh"? ...
+ not sure if the order of this matters, but write net -> stdout first. */
+
+ /* sanity check. Works because they're both unsigned... */
+ if ((rzleft > 8200) || (rnleft > 8200)) {
+ holler_error("bogus buffers: %u, %u", rzleft, rnleft);
+ rzleft = rnleft = 0;
+ }
+ /* net write retries sometimes happen on UDP connections */
+ if (!wretry) { /* is something hung? */
+ holler_error("too many output retries");
+ return 1;
+ }
+ if (rnleft) {
+ rr = write(STDOUT_FILENO, np, rnleft);
+ if (rr > 0) {
+ if (o_ofile) /* log the stdout */
+ oprint('<', (unsigned char *)np, rr);
+ np += rr; /* fix up ptrs and whatnot */
+ rnleft -= rr; /* will get sanity-checked above */
+ wrote_out += rr; /* global count */
+ }
+Debug("wrote %d to stdout, errno %d", rr, errno);
+ } /* rnleft */
+ if (rzleft) {
+ if (o_interval) /* in "slowly" mode ?? */
+ rr = findline(zp, rzleft);
+ else
+ rr = rzleft;
+ rr = write(netfd, zp, rr); /* one line, or the whole buffer */
+ if (rr > 0) {
+ if (o_ofile) /* log what got sent */
+ oprint('>', (unsigned char *)zp, rr);
+ zp += rr;
+ rzleft -= rr;
+ wrote_net += rr; /* global count */
+ }
+Debug("wrote %d to net, errno %d", rr, errno);
+ } /* rzleft */
+ if (o_interval) { /* cycle between slow lines, or ... */
+ sleep(o_interval);
+ errno = 0; /* clear from sleep */
+ continue; /* ...with hairy select loop... */
+ }
+ if ((rzleft) || (rnleft)) { /* shovel that shit till they ain't */
+ wretry--; /* none left, and get another load */
+ goto shovel;
+ }
+ } /* while ding1:netfd is open */
+
+ /* XXX: maybe want a more graceful shutdown() here, or screw around with
+ linger times?? I suspect that I don't need to since I'm always doing
+ blocking reads and writes and my own manual "last ditch" efforts to read
+ the net again after a timeout. I haven't seen any screwups yet, but it's
+ not like my test network is particularly busy... */
+ close(netfd);
+ return 0;
+} /* readwrite */
+
+/* main: now we pull it all together... */
+int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nc_main(int argc, char **argv)
+{
+ char *str_p, *str_s;
+ USE_NC_EXTRA(char *str_i, *str_o;)
+ char *themdotted = themdotted; /* gcc */
+ char **proggie;
+ int x;
+ unsigned o_lport = 0;
+
+ INIT_G();
+
+ /* catch a signal or two for cleanup */
+ bb_signals(0
+ + (1 << SIGINT)
+ + (1 << SIGQUIT)
+ + (1 << SIGTERM)
+ , catch);
+ /* and suppress others... */
+ bb_signals(0
+#ifdef SIGURG
+ + (1 << SIGURG)
+#endif
+ + (1 << SIGPIPE) /* important! */
+ , SIG_IGN);
+
+ proggie = argv;
+ while (*++proggie) {
+ if (strcmp(*proggie, "-e") == 0) {
+ *proggie = NULL;
+ argc = proggie - argv;
+ proggie++;
+ goto e_found;
+ }
+ }
+ proggie = NULL;
+ e_found:
+
+ // -g -G -t -r deleted, unimplemented -a deleted too
+ opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */
+ getopt32(argv, "hnp:s:uvw:" USE_NC_SERVER("l")
+ USE_NC_EXTRA("i:o:z"),
+ &str_p, &str_s, &o_wait
+ USE_NC_EXTRA(, &str_i, &str_o, &o_verbose));
+ argv += optind;
+#if ENABLE_NC_EXTRA
+ if (option_mask32 & OPT_i) /* line-interval time */
+ o_interval = xatou_range(str_i, 1, 0xffff);
+#endif
+ //if (option_mask32 & OPT_l) /* listen mode */
+ //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
+ //if (option_mask32 & OPT_o) /* hexdump log */
+ if (option_mask32 & OPT_p) { /* local source port */
+ o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
+ if (!o_lport)
+ bb_error_msg_and_die("bad local port '%s'", str_p);
+ }
+ //if (option_mask32 & OPT_r) /* randomize various things */
+ //if (option_mask32 & OPT_u) /* use UDP */
+ //if (option_mask32 & OPT_v) /* verbose */
+ //if (option_mask32 & OPT_w) /* wait time */
+ //if (option_mask32 & OPT_z) /* little or no data xfer */
+
+ /* We manage our fd's so that they are never 0,1,2 */
+ /*bb_sanitize_stdio(); - not needed */
+
+ if (argv[0]) {
+ themaddr = xhost2sockaddr(argv[0],
+ argv[1]
+ ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
+ : 0);
+ }
+
+ /* create & bind network socket */
+ x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM);
+ if (option_mask32 & OPT_s) { /* local address */
+ /* if o_lport is still 0, then we will use random port */
+ ouraddr = xhost2sockaddr(str_s, o_lport);
+#ifdef BLOAT
+ /* prevent spurious "UDP listen needs !0 port" */
+ o_lport = get_nport(ouraddr);
+ o_lport = ntohs(o_lport);
+#endif
+ x = xsocket(ouraddr->u.sa.sa_family, x, 0);
+ } else {
+ /* We try IPv6, then IPv4, unless addr family is
+ * implicitly set by way of remote addr/port spec */
+ x = xsocket_type(&ouraddr,
+ (themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
+ x);
+ if (o_lport)
+ set_nport(ouraddr, htons(o_lport));
+ }
+ xmove_fd(x, netfd);
+ setsockopt_reuseaddr(netfd);
+ if (o_udpmode)
+ socket_want_pktinfo(netfd);
+ xbind(netfd, &ouraddr->u.sa, ouraddr->len);
+#if 0
+ setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
+ setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
+#endif
+
+#ifdef BLOAT
+ if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
+ /* apparently UDP can listen ON "port 0",
+ but that's not useful */
+ if (!o_lport)
+ bb_error_msg_and_die("UDP listen needs nonzero -p port");
+ }
+#endif
+
+ FD_SET(STDIN_FILENO, &ding1); /* stdin *is* initially open */
+ if (proggie) {
+ close(0); /* won't need stdin */
+ option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
+ }
+#if ENABLE_NC_EXTRA
+ if (o_ofile)
+ xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
+#endif
+
+ if (o_listen) {
+ dolisten();
+ /* dolisten does its own connect reporting */
+ if (proggie) /* -e given? */
+ doexec(proggie);
+ x = readwrite(); /* it even works with UDP! */
+ } else {
+ /* Outbound connects. Now we're more picky about args... */
+ if (!themaddr)
+ bb_error_msg_and_die("no destination");
+
+ remend = *themaddr;
+ if (o_verbose)
+ themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa);
+
+ x = connect_w_timeout(netfd);
+ if (o_zero && x == 0 && o_udpmode) /* if UDP scanning... */
+ x = udptest();
+ if (x == 0) { /* Yow, are we OPEN YET?! */
+ if (o_verbose)
+ fprintf(stderr, "%s (%s) open\n", argv[0], themdotted);
+ if (proggie) /* exec is valid for outbound, too */
+ doexec(proggie);
+ if (!o_zero)
+ x = readwrite();
+ } else { /* connect or udptest wasn't successful */
+ x = 1; /* exit status */
+ /* if we're scanning at a "one -v" verbosity level, don't print refusals.
+ Give it another -v if you want to see everything. */
+ if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED))
+ bb_perror_msg("%s (%s)", argv[0], themdotted);
+ }
+ }
+ if (o_verbose > 1) /* normally we don't care */
+ fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
+ return x;
+}
diff --git a/release/src/router/busybox/networking/netstat.c b/release/src/router/busybox/networking/netstat.c
index 17a58876..b2462806 100644
--- a/release/src/router/busybox/networking/netstat.c
+++ b/release/src/router/busybox/networking/netstat.c
@@ -5,432 +5,511 @@
*
* Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* 2002-04-20
* IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ *
+ * 2008-07-10
+ * optional '-p' flag support ported from net-tools by G. Somlo <somlo@cmu.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <unistd.h>
+#include "libbb.h"
#include "inet_common.h"
-#include "busybox.h"
-#include "pwd_.h"
-
-#ifdef CONFIG_ROUTE
-extern void displayroutes(int noresolve, int netstatfmt);
-#endif
-#define NETSTAT_CONNECTED 0x01
-#define NETSTAT_LISTENING 0x02
-#define NETSTAT_NUMERIC 0x04
-#define NETSTAT_TCP 0x10
-#define NETSTAT_UDP 0x20
-#define NETSTAT_RAW 0x40
-#define NETSTAT_UNIX 0x80
+#define NETSTAT_OPTS "laentuwx" \
+ USE_ROUTE( "r") \
+ USE_FEATURE_NETSTAT_WIDE("W") \
+ USE_FEATURE_NETSTAT_PRG( "p")
-static int flags = NETSTAT_CONNECTED |
- NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
+enum {
+ OPTBIT_KEEP_OLD = 7,
+ USE_ROUTE( OPTBIT_ROUTE,)
+ USE_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
+ USE_FEATURE_NETSTAT_PRG( OPTBIT_PRG ,)
+ OPT_sock_listen = 1 << 0, // l
+ OPT_sock_all = 1 << 1, // a
+ OPT_extended = 1 << 2, // e
+ OPT_noresolve = 1 << 3, // n
+ OPT_sock_tcp = 1 << 4, // t
+ OPT_sock_udp = 1 << 5, // u
+ OPT_sock_raw = 1 << 6, // w
+ OPT_sock_unix = 1 << 7, // x
+ OPT_route = USE_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r
+ OPT_wide = USE_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W
+ OPT_prg = USE_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p
+};
-#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
-#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
-#define PROGNAME_WIDTH2(s) #s
+#define NETSTAT_CONNECTED 0x01
+#define NETSTAT_LISTENING 0x02
+#define NETSTAT_NUMERIC 0x04
+/* Must match getopt32 option string */
+#define NETSTAT_TCP 0x10
+#define NETSTAT_UDP 0x20
+#define NETSTAT_RAW 0x40
+#define NETSTAT_UNIX 0x80
+#define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
-#define PRG_HASH_SIZE 211
enum {
- TCP_ESTABLISHED = 1,
- TCP_SYN_SENT,
- TCP_SYN_RECV,
- TCP_FIN_WAIT1,
- TCP_FIN_WAIT2,
- TCP_TIME_WAIT,
- TCP_CLOSE,
- TCP_CLOSE_WAIT,
- TCP_LAST_ACK,
- TCP_LISTEN,
- TCP_CLOSING /* now a valid state */
+ TCP_ESTABLISHED = 1,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN,
+ TCP_CLOSING, /* now a valid state */
};
-static const char * const tcp_state[] =
-{
- "",
- "ESTABLISHED",
- "SYN_SENT",
- "SYN_RECV",
- "FIN_WAIT1",
- "FIN_WAIT2",
- "TIME_WAIT",
- "CLOSE",
- "CLOSE_WAIT",
- "LAST_ACK",
- "LISTEN",
- "CLOSING"
+static const char *const tcp_state[] = {
+ "",
+ "ESTABLISHED",
+ "SYN_SENT",
+ "SYN_RECV",
+ "FIN_WAIT1",
+ "FIN_WAIT2",
+ "TIME_WAIT",
+ "CLOSE",
+ "CLOSE_WAIT",
+ "LAST_ACK",
+ "LISTEN",
+ "CLOSING"
};
typedef enum {
- SS_FREE = 0, /* not allocated */
- SS_UNCONNECTED, /* unconnected to any socket */
- SS_CONNECTING, /* in process of connecting */
- SS_CONNECTED, /* connected to socket */
- SS_DISCONNECTING /* in process of disconnecting */
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING /* in process of disconnecting */
} socket_state;
-#define SO_ACCEPTCON (1<<16) /* performed a listen */
-#define SO_WAITDATA (1<<17) /* wait data to read */
-#define SO_NOSPACE (1<<18) /* no space to write */
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+#define SO_WAITDATA (1<<17) /* wait data to read */
+#define SO_NOSPACE (1<<18) /* no space to write */
-static char *itoa(unsigned int i)
-{
- /* 21 digits plus null terminator, good for 64-bit or smaller ints */
- static char local[22];
- char *p = &local[21];
- *p-- = '\0';
- do {
- *p-- = '0' + i % 10;
- i /= 10;
- } while (i > 0);
- return p + 1;
-}
+/* Standard printout size */
+#define PRINT_IP_MAX_SIZE 23
+#define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s"
+#define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State "
+
+/* When there are IPv6 connections the IPv6 addresses will be
+ * truncated to none-recognition. The '-W' option makes the
+ * address columns wide enough to accomodate for longest possible
+ * IPv6 addresses, i.e. addresses of the form
+ * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd
+ */
+#define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */
+#define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s"
+#define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State "
+
+
+#define PROGNAME_WIDTH 20
+#define PROGNAME_WIDTH_STR "20"
+/* PROGNAME_WIDTH chars: 12345678901234567890 */
+#define PROGNAME_BANNER "PID/Program name "
+
+struct prg_node {
+ struct prg_node *next;
+ long inode;
+ char name[PROGNAME_WIDTH];
+};
+
+#define PRG_HASH_SIZE 211
-static char *get_sname(int port, const char *proto, int num)
+
+struct globals {
+ const char *net_conn_line;
+ smallint flags;
+#if ENABLE_FEATURE_NETSTAT_PRG
+ smallint prg_cache_loaded;
+ struct prg_node *prg_hash[PRG_HASH_SIZE];
+#endif
+};
+#define G (*ptr_to_globals)
+#define flags (G.flags )
+#define net_conn_line (G.net_conn_line )
+#define prg_hash (G.prg_hash )
+#define prg_cache_loaded (G.prg_cache_loaded)
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+ flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \
+ net_conn_line = PRINT_NET_CONN; \
+} while (0)
+
+
+#if ENABLE_FEATURE_NETSTAT_PRG
+
+/* Deliberately truncating long to unsigned *int* */
+#define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE)
+
+#define print_progname_banner() do { \
+ if (option_mask32 & OPT_prg) printf(PROGNAME_BANNER); \
+} while (0)
+
+static void prg_cache_add(long inode, char *name)
{
- char *str=itoa(ntohs(port));
- if (num) {
- } else {
- struct servent *se=getservbyport(port,proto);
- if (se)
- str=se->s_name;
- }
- if (!port) {
- str="*";
+ unsigned hi = PRG_HASHIT(inode);
+ struct prg_node **pnp, *pn;
+
+ prg_cache_loaded = 2;
+ for (pnp = prg_hash + hi; (pn = *pnp) != NULL; pnp = &pn->next) {
+ if (pn->inode == inode) {
+ /* Some warning should be appropriate here
+ as we got multiple processes for one i-node */
+ return;
+ }
}
- return str;
+ *pnp = xzalloc(sizeof(struct prg_node));
+ pn = *pnp;
+ pn->inode = inode;
+ safe_strncpy(pn->name, name, PROGNAME_WIDTH);
}
-static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
+static const char *prg_cache_get(long inode)
{
- char *port_name;
+ unsigned hi = PRG_HASHIT(inode);
+ struct prg_node *pn;
-#ifdef CONFIG_FEATURE_IPV6
- if (addr->sa_family == AF_INET6) {
- INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
- (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0);
- } else
-#endif
- {
- INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
- 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
- 0xffffffff);
- }
- port_name=get_sname(htons(port), proto, numeric);
- if ((strlen(ip_port) + strlen(port_name)) > 22)
- ip_port[22 - strlen(port_name)] = '\0';
- ip_port+=strlen(ip_port);
- strcat(ip_port, ":");
- strcat(ip_port, port_name);
+ for (pn = prg_hash[hi]; pn; pn = pn->next)
+ if (pn->inode == inode)
+ return pn->name;
+ return "-";
}
-static void tcp_do_one(int lnr, const char *line)
+#if ENABLE_FEATURE_CLEAN_UP
+static void prg_cache_clear(void)
{
- char local_addr[64], rem_addr[64];
- const char *state_str;
- char more[512];
- int num, local_port, rem_port, d, state, timer_run, uid, timeout;
-#ifdef CONFIG_FEATURE_IPV6
- struct sockaddr_in6 localaddr, remaddr;
- char addr6[INET6_ADDRSTRLEN];
- struct in6_addr in6;
+ struct prg_node **pnp, *pn;
+
+ for (pnp = prg_hash; pnp < prg_hash + PRG_HASH_SIZE; pnp++) {
+ while ((pn = *pnp) != NULL) {
+ *pnp = pn->next;
+ free(pn);
+ }
+ }
+}
#else
- struct sockaddr_in localaddr, remaddr;
+#define prg_cache_clear() ((void)0)
#endif
- unsigned long rxq, txq, time_len, retr, inode;
-
- if (lnr == 0)
- return;
- more[0] = '\0';
- num = sscanf(line,
- "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
- &d, local_addr, &local_port,
- rem_addr, &rem_port, &state,
- &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+static long extract_socket_inode(const char *lname)
+{
+ long inode = -1;
+
+ if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
+ /* "socket:[12345]", extract the "12345" as inode */
+ inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0);
+ if (*lname != ']')
+ inode = -1;
+ } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) {
+ /* "[0000]:12345", extract the "12345" as inode */
+ inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0);
+ if (errno) /* not NUL terminated? */
+ inode = -1;
+ }
- if (strlen(local_addr) > 8) {
-#ifdef CONFIG_FEATURE_IPV6
- sscanf(local_addr, "%08X%08X%08X%08X",
- &in6.s6_addr32[0], &in6.s6_addr32[1],
- &in6.s6_addr32[2], &in6.s6_addr32[3]);
- inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
- sscanf(rem_addr, "%08X%08X%08X%08X",
- &in6.s6_addr32[0], &in6.s6_addr32[1],
- &in6.s6_addr32[2], &in6.s6_addr32[3]);
- inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
- localaddr.sin6_family = AF_INET6;
- remaddr.sin6_family = AF_INET6;
+#if 0 /* bb_strtol returns all-ones bit pattern on ERANGE anyway */
+ if (errno == ERANGE)
+ inode = -1;
#endif
- } else {
- sscanf(local_addr, "%X",
- &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
- sscanf(rem_addr, "%X",
- &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
- ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
- ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
- }
+ return inode;
+}
- if (num < 10) {
- bb_error_msg("warning, got bogus tcp line.");
- return;
+static int FAST_FUNC file_act(const char *fileName,
+ struct stat *statbuf UNUSED_PARAM,
+ void *userData,
+ int depth UNUSED_PARAM)
+{
+ char *linkname;
+ long inode;
+
+ linkname = xmalloc_readlink(fileName);
+ if (linkname != NULL) {
+ inode = extract_socket_inode(linkname);
+ free(linkname);
+ if (inode >= 0)
+ prg_cache_add(inode, (char *)userData);
}
- state_str = tcp_state[state];
- if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
- (!rem_port && (flags&NETSTAT_LISTENING)))
- {
- snprint_ip_port(local_addr, sizeof(local_addr),
- (struct sockaddr *) &localaddr, local_port,
- "tcp", flags&NETSTAT_NUMERIC);
-
- snprint_ip_port(rem_addr, sizeof(rem_addr),
- (struct sockaddr *) &remaddr, rem_port,
- "tcp", flags&NETSTAT_NUMERIC);
-
- printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
- rxq, txq, local_addr, rem_addr, state_str);
+ return TRUE;
+}
- }
+static int FAST_FUNC dir_act(const char *fileName,
+ struct stat *statbuf UNUSED_PARAM,
+ void *userData UNUSED_PARAM,
+ int depth)
+{
+ const char *shortName;
+ char *p, *q;
+ char cmdline_buf[512];
+ int i;
+
+ if (depth == 0) /* "/proc" itself */
+ return TRUE; /* continue looking one level below /proc */
+
+ shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
+ if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */
+ return SKIP;
+
+ p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */
+ i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1);
+ free(p);
+ if (i < 0)
+ return FALSE;
+ cmdline_buf[i] = '\0';
+ q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */
+
+ /* go through all files in /proc/PID/fd */
+ p = concat_path_file(fileName, "fd");
+ i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET,
+ file_act, NULL, (void *)q, 0);
+
+ free(p);
+ free(q);
+
+ if (!i)
+ return FALSE; /* signal permissions error to caller */
+
+ return SKIP; /* caller should not recurse further into this dir. */
}
-static void udp_do_one(int lnr, const char *line)
+static void prg_cache_load(void)
{
- char local_addr[64], rem_addr[64];
- char *state_str, more[512];
- int num, local_port, rem_port, d, state, timer_run, uid, timeout;
-#ifdef CONFIG_FEATURE_IPV6
- struct sockaddr_in6 localaddr, remaddr;
- char addr6[INET6_ADDRSTRLEN];
- struct in6_addr in6;
-#else
- struct sockaddr_in localaddr, remaddr;
-#endif
- unsigned long rxq, txq, time_len, retr, inode;
+ int load_ok;
- if (lnr == 0)
+ prg_cache_loaded = 1;
+ load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET,
+ NULL, dir_act, NULL, 0);
+ if (load_ok)
return;
- more[0] = '\0';
- num = sscanf(line,
- "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
- &d, local_addr, &local_port,
- rem_addr, &rem_port, &state,
- &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+ if (prg_cache_loaded == 1)
+ bb_error_msg("can't scan /proc - are you root?");
+ else
+ bb_error_msg("showing only processes with your user ID");
+}
- if (strlen(local_addr) > 8) {
-#ifdef CONFIG_FEATURE_IPV6
- /* Demangle what the kernel gives us */
- sscanf(local_addr, "%08X%08X%08X%08X",
- &in6.s6_addr32[0], &in6.s6_addr32[1],
- &in6.s6_addr32[2], &in6.s6_addr32[3]);
- inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
- sscanf(rem_addr, "%08X%08X%08X%08X",
- &in6.s6_addr32[0], &in6.s6_addr32[1],
- &in6.s6_addr32[2], &in6.s6_addr32[3]);
- inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
- localaddr.sin6_family = AF_INET6;
- remaddr.sin6_family = AF_INET6;
-#endif
- } else {
- sscanf(local_addr, "%X",
- &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
- sscanf(rem_addr, "%X",
- &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
- ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
- ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
- }
+#else
- if (num < 10) {
- bb_error_msg("warning, got bogus udp line.");
- return;
- }
- switch (state) {
- case TCP_ESTABLISHED:
- state_str = "ESTABLISHED";
- break;
+#define prg_cache_clear() ((void)0)
+#define print_progname_banner() ((void)0)
- case TCP_CLOSE:
- state_str = "";
- break;
+#endif //ENABLE_FEATURE_NETSTAT_PRG
- default:
- state_str = "UNKNOWN";
- break;
- }
-#ifdef CONFIG_FEATURE_IPV6
-# define notnull(A) (((A.sin6_family == AF_INET6) && \
- ((A.sin6_addr.s6_addr32[0]) || \
- (A.sin6_addr.s6_addr32[1]) || \
- (A.sin6_addr.s6_addr32[2]) || \
- (A.sin6_addr.s6_addr32[3]))) || \
- ((A.sin6_family == AF_INET) && \
- ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#if ENABLE_FEATURE_IPV6
+static void build_ipv6_addr(char* local_addr, struct sockaddr_in6* localaddr)
+{
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr->sin6_addr);
+
+ localaddr->sin6_family = AF_INET6;
+}
+#endif
+
+#if ENABLE_FEATURE_IPV6
+static void build_ipv4_addr(char* local_addr, struct sockaddr_in6* localaddr)
#else
-# define notnull(A) (A.sin_addr.s_addr)
+static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr)
#endif
- if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
- (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
- {
- snprint_ip_port(local_addr, sizeof(local_addr),
- (struct sockaddr *) &localaddr, local_port,
- "udp", flags&NETSTAT_NUMERIC);
-
- snprint_ip_port(rem_addr, sizeof(rem_addr),
- (struct sockaddr *) &remaddr, rem_port,
- "udp", flags&NETSTAT_NUMERIC);
-
- printf("udp %6ld %6ld %-23s %-23s %-12s\n",
- rxq, txq, local_addr, rem_addr, state_str);
+{
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) localaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) localaddr)->sa_family = AF_INET;
+}
+static const char *get_sname(int port, const char *proto, int numeric)
+{
+ if (!port)
+ return "*";
+ if (!numeric) {
+ struct servent *se = getservbyport(port, proto);
+ if (se)
+ return se->s_name;
}
+ /* hummm, we may return static buffer here!! */
+ return itoa(ntohs(port));
}
-static void raw_do_one(int lnr, const char *line)
+static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric)
{
- char local_addr[64], rem_addr[64];
- char *state_str, more[512];
- int num, local_port, rem_port, d, state, timer_run, uid, timeout;
-#ifdef CONFIG_FEATURE_IPV6
+ char *host, *host_port;
+
+ /* Code which used "*" for INADDR_ANY is removed: it's ambiguous
+ * in IPv6, while "0.0.0.0" is not. */
+
+ host = numeric ? xmalloc_sockaddr2dotted_noport(addr)
+ : xmalloc_sockaddr2host_noport(addr);
+
+ host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric));
+ free(host);
+ return host_port;
+}
+
+struct inet_params {
+ int local_port, rem_port, state, uid;
+#if ENABLE_FEATURE_IPV6
struct sockaddr_in6 localaddr, remaddr;
- char addr6[INET6_ADDRSTRLEN];
- struct in6_addr in6;
#else
struct sockaddr_in localaddr, remaddr;
#endif
- unsigned long rxq, txq, time_len, retr, inode;
+ unsigned long rxq, txq, inode;
+};
- if (lnr == 0)
- return;
+static int scan_inet_proc_line(struct inet_params *param, char *line)
+{
+ int num;
+ char local_addr[64], rem_addr[64];
- more[0] = '\0';
num = sscanf(line,
- "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
- &d, local_addr, &local_port,
- rem_addr, &rem_port, &state,
- &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+ "%*d: %64[0-9A-Fa-f]:%X "
+ "%64[0-9A-Fa-f]:%X %X "
+ "%lX:%lX %*X:%*X "
+ "%*X %d %*d %ld ",
+ local_addr, &param->local_port,
+ rem_addr, &param->rem_port, &param->state,
+ &param->txq, &param->rxq,
+ &param->uid, &param->inode);
+ if (num < 9) {
+ return 1; /* error */
+ }
if (strlen(local_addr) > 8) {
-#ifdef CONFIG_FEATURE_IPV6
- sscanf(local_addr, "%08X%08X%08X%08X",
- &in6.s6_addr32[0], &in6.s6_addr32[1],
- &in6.s6_addr32[2], &in6.s6_addr32[3]);
- inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
- sscanf(rem_addr, "%08X%08X%08X%08X",
- &in6.s6_addr32[0], &in6.s6_addr32[1],
- &in6.s6_addr32[2], &in6.s6_addr32[3]);
- inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
- localaddr.sin6_family = AF_INET6;
- remaddr.sin6_family = AF_INET6;
+#if ENABLE_FEATURE_IPV6
+ build_ipv6_addr(local_addr, &param->localaddr);
+ build_ipv6_addr(rem_addr, &param->remaddr);
#endif
} else {
- sscanf(local_addr, "%X",
- &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
- sscanf(rem_addr, "%X",
- &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
- ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
- ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ build_ipv4_addr(local_addr, &param->localaddr);
+ build_ipv4_addr(rem_addr, &param->remaddr);
}
+ return 0;
+}
- if (num < 10) {
- bb_error_msg("warning, got bogus raw line.");
- return;
+static void print_inet_line(struct inet_params *param,
+ const char *state_str, const char *proto, int is_connected)
+{
+ if ((is_connected && (flags & NETSTAT_CONNECTED))
+ || (!is_connected && (flags & NETSTAT_LISTENING))
+ ) {
+ char *l = ip_port_str(
+ (struct sockaddr *) &param->localaddr, param->local_port,
+ proto, flags & NETSTAT_NUMERIC);
+ char *r = ip_port_str(
+ (struct sockaddr *) &param->remaddr, param->rem_port,
+ proto, flags & NETSTAT_NUMERIC);
+ printf(net_conn_line,
+ proto, param->rxq, param->txq, l, r, state_str);
+#if ENABLE_FEATURE_NETSTAT_PRG
+ if (option_mask32 & OPT_prg)
+ printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode));
+#endif
+ bb_putchar('\n');
+ free(l);
+ free(r);
}
- state_str=itoa(state);
-
-#ifdef CONFIG_FEATURE_IPV6
-# define notnull(A) (((A.sin6_family == AF_INET6) && \
- ((A.sin6_addr.s6_addr32[0]) || \
- (A.sin6_addr.s6_addr32[1]) || \
- (A.sin6_addr.s6_addr32[2]) || \
- (A.sin6_addr.s6_addr32[3]))) || \
- ((A.sin6_family == AF_INET) && \
- ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+}
+
+static int FAST_FUNC tcp_do_one(char *line)
+{
+ struct inet_params param;
+
+ if (scan_inet_proc_line(&param, line))
+ return 1;
+
+ print_inet_line(&param, tcp_state[param.state], "tcp", param.rem_port);
+ return 0;
+}
+
+#if ENABLE_FEATURE_IPV6
+# define notnull(A) ( \
+ ( (A.sin6_family == AF_INET6) \
+ && (A.sin6_addr.s6_addr32[0] | A.sin6_addr.s6_addr32[1] | \
+ A.sin6_addr.s6_addr32[2] | A.sin6_addr.s6_addr32[3]) \
+ ) || ( \
+ (A.sin6_family == AF_INET) \
+ && ((struct sockaddr_in*)&A)->sin_addr.s_addr \
+ ) \
+)
#else
# define notnull(A) (A.sin_addr.s_addr)
#endif
- if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
- (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
- {
- snprint_ip_port(local_addr, sizeof(local_addr),
- (struct sockaddr *) &localaddr, local_port,
- "raw", flags&NETSTAT_NUMERIC);
-
- snprint_ip_port(rem_addr, sizeof(rem_addr),
- (struct sockaddr *) &remaddr, rem_port,
- "raw", flags&NETSTAT_NUMERIC);
-
- printf("raw %6ld %6ld %-23s %-23s %-12s\n",
- rxq, txq, local_addr, rem_addr, state_str);
+static int FAST_FUNC udp_do_one(char *line)
+{
+ int have_remaddr;
+ const char *state_str;
+ struct inet_params param;
+
+ if (scan_inet_proc_line(&param, line))
+ return 1;
+
+ state_str = "UNKNOWN";
+ switch (param.state) {
+ case TCP_ESTABLISHED:
+ state_str = "ESTABLISHED";
+ break;
+ case TCP_CLOSE:
+ state_str = "";
+ break;
}
+
+ have_remaddr = notnull(param.remaddr);
+ print_inet_line(&param, state_str, "udp", have_remaddr);
+ return 0;
}
-#define HAS_INODE 1
+static int FAST_FUNC raw_do_one(char *line)
+{
+ int have_remaddr;
+ struct inet_params param;
+
+ if (scan_inet_proc_line(&param, line))
+ return 1;
-static void unix_do_one(int nr, const char *line)
+ have_remaddr = notnull(param.remaddr);
+ print_inet_line(&param, itoa(param.state), "raw", have_remaddr);
+ return 0;
+}
+
+static int FAST_FUNC unix_do_one(char *line)
{
- static int has = 0;
- char path[PATH_MAX], ss_flags[32];
- char *ss_proto, *ss_state, *ss_type;
- int num, state, type, inode;
- void *d;
unsigned long refcnt, proto, unix_flags;
-
- if (nr == 0) {
- if (strstr(line, "Inode"))
- has |= HAS_INODE;
- return;
- }
- path[0] = '\0';
- num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
- &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
+ unsigned long inode;
+ int type, state;
+ int num, path_ofs;
+ const char *ss_proto, *ss_state, *ss_type;
+ char ss_flags[32];
+
+ /* 2.6.15 may report lines like "... @/tmp/fam-user-^@^@^@^@^@^@^@..."
+ * Other users report long lines filled by NUL bytes.
+ * (those ^@ are NUL bytes too). We see them as empty lines. */
+ if (!line[0])
+ return 0;
+
+ path_ofs = 0; /* paranoia */
+ num = sscanf(line, "%*p: %lX %lX %lX %X %X %lu %n",
+ &refcnt, &proto, &unix_flags, &type, &state, &inode, &path_ofs);
if (num < 6) {
- bb_error_msg("warning, got bogus unix line.");
- return;
+ return 1; /* error */
}
- if (!(has & HAS_INODE))
- snprintf(path,sizeof(path),"%d",inode);
-
- if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
+ if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
- if (!(flags&NETSTAT_LISTENING))
- return;
+ if (!(flags & NETSTAT_LISTENING))
+ return 0;
} else {
- if (!(flags&NETSTAT_CONNECTED))
- return;
+ if (!(flags & NETSTAT_CONNECTED))
+ return 0;
}
}
@@ -438,7 +517,6 @@ static void unix_do_one(int nr, const char *line)
case 0:
ss_proto = "unix";
break;
-
default:
ss_proto = "??";
}
@@ -447,23 +525,18 @@ static void unix_do_one(int nr, const char *line)
case SOCK_STREAM:
ss_type = "STREAM";
break;
-
case SOCK_DGRAM:
ss_type = "DGRAM";
break;
-
case SOCK_RAW:
ss_type = "RAW";
break;
-
case SOCK_RDM:
ss_type = "RDM";
break;
-
case SOCK_SEQPACKET:
ss_type = "SEQPACKET";
break;
-
default:
ss_type = "UNKNOWN";
}
@@ -472,7 +545,6 @@ static void unix_do_one(int nr, const char *line)
case SS_FREE:
ss_state = "FREE";
break;
-
case SS_UNCONNECTED:
/*
* Unconnected sockets may be listening
@@ -484,19 +556,15 @@ static void unix_do_one(int nr, const char *line)
ss_state = "";
}
break;
-
case SS_CONNECTING:
ss_state = "CONNECTING";
break;
-
case SS_CONNECTED:
ss_state = "CONNECTED";
break;
-
case SS_DISCONNECTING:
ss_state = "DISCONNECTING";
break;
-
default:
ss_state = "UNKNOWN";
}
@@ -508,154 +576,137 @@ static void unix_do_one(int nr, const char *line)
strcat(ss_flags, "W ");
if (unix_flags & SO_NOSPACE)
strcat(ss_flags, "N ");
-
strcat(ss_flags, "]");
- printf("%-5s %-6ld %-11s %-10s %-13s ",
- ss_proto, refcnt, ss_flags, ss_type, ss_state);
- if (has & HAS_INODE)
- printf("%-6d ",inode);
- else
- printf("- ");
- puts(path);
-}
+ printf("%-5s %-6ld %-11s %-10s %-13s %6lu ",
+ ss_proto, refcnt, ss_flags, ss_type, ss_state, inode
+ );
-#define _PATH_PROCNET_UDP "/proc/net/udp"
-#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
-#define _PATH_PROCNET_TCP "/proc/net/tcp"
-#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
-#define _PATH_PROCNET_RAW "/proc/net/raw"
-#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
-#define _PATH_PROCNET_UNIX "/proc/net/unix"
+#if ENABLE_FEATURE_NETSTAT_PRG
+ if (option_mask32 & OPT_prg)
+ printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode));
+#endif
-static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
+ /* TODO: currently we stop at first NUL byte. Is it a problem? */
+ line += path_ofs;
+ *strchrnul(line, '\n') = '\0';
+ while (*line)
+ fputc_printable(*line++, stdout);
+ bb_putchar('\n');
+ return 0;
+}
+
+static void do_info(const char *file, int FAST_FUNC (*proc)(char *))
{
- char buffer[8192];
- int lnr = 0;
+ int lnr;
FILE *procinfo;
+ char *buffer;
- procinfo = fopen(file, "r");
+ /* _stdin is just to save "r" param */
+ procinfo = fopen_or_warn_stdin(file);
if (procinfo == NULL) {
- if (errno != ENOENT) {
- perror(file);
- } else {
- bb_error_msg("no support for `%s' on this system.", name);
- }
- } else {
- do {
- if (fgets(buffer, sizeof(buffer), procinfo))
- (proc)(lnr++, buffer);
- } while (!feof(procinfo));
- fclose(procinfo);
+ return;
+ }
+ lnr = 0;
+ /* Why xmalloc_fgets_str? because it doesn't stop on NULs */
+ while ((buffer = xmalloc_fgets_str(procinfo, "\n")) != NULL) {
+ /* line 0 is skipped */
+ if (lnr && proc(buffer))
+ bb_error_msg("%s: bogus data on line %d", file, lnr + 1);
+ lnr++;
+ free(buffer);
}
+ fclose(procinfo);
}
-/*
- * Our main function.
- */
-
-int netstat_main(int argc, char **argv)
+int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int netstat_main(int argc UNUSED_PARAM, char **argv)
{
- int opt;
- int new_flags=0;
- int showroute = 0, extended = 0;
-#ifdef CONFIG_FEATURE_IPV6
- int inet=1;
- int inet6=1;
+ const char *net_conn_line_header = PRINT_NET_CONN_HEADER;
+ unsigned opt;
+
+ INIT_G();
+
+ /* Option string must match NETSTAT_xxx constants */
+ opt = getopt32(argv, NETSTAT_OPTS);
+ if (opt & 0x1) { // -l
+ flags &= ~NETSTAT_CONNECTED;
+ flags |= NETSTAT_LISTENING;
+ }
+ if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
+ //if (opt & 0x4) // -e
+ if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
+ //if (opt & 0x10) // -t: NETSTAT_TCP
+ //if (opt & 0x20) // -u: NETSTAT_UDP
+ //if (opt & 0x40) // -w: NETSTAT_RAW
+ //if (opt & 0x80) // -x: NETSTAT_UNIX
+ if (opt & OPT_route) { // -r
+#if ENABLE_ROUTE
+ bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
+ return 0;
#else
-# define inet 1
-# define inet6 0
+ bb_show_usage();
#endif
- while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
- switch (opt) {
- case 'l':
- flags &= ~NETSTAT_CONNECTED;
- flags |= NETSTAT_LISTENING;
- break;
- case 'a':
- flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
- break;
- case 'n':
- flags |= NETSTAT_NUMERIC;
- break;
- case 'r':
- showroute = 1;
- break;
- case 'e':
- extended = 1;
- break;
- case 't':
- new_flags |= NETSTAT_TCP;
- break;
- case 'u':
- new_flags |= NETSTAT_UDP;
- break;
- case 'w':
- new_flags |= NETSTAT_RAW;
- break;
- case 'x':
- new_flags |= NETSTAT_UNIX;
- break;
- default:
- bb_show_usage();
- }
- if ( showroute ) {
-#ifdef CONFIG_ROUTE
- displayroutes ( flags & NETSTAT_NUMERIC, !extended );
- return 0;
-#else
- bb_error_msg_and_die( "-r (display routing table) is not compiled in." );
+ }
+ if (opt & OPT_wide) { // -W
+ net_conn_line = PRINT_NET_CONN_WIDE;
+ net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE;
+ }
+#if ENABLE_FEATURE_NETSTAT_PRG
+ if (opt & OPT_prg) { // -p
+ prg_cache_load();
+ }
#endif
- }
-
- if (new_flags) {
- flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
- flags |= new_flags;
+
+ opt &= NETSTAT_ALLPROTO;
+ if (opt) {
+ flags &= ~NETSTAT_ALLPROTO;
+ flags |= opt;
}
- if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
+ if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
printf("Active Internet connections "); /* xxx */
- if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
+ if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
printf("(servers and established)");
- else {
- if (flags&NETSTAT_LISTENING)
- printf("(only servers)");
- else
- printf("(w/o servers)");
- }
- printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
+ else if (flags & NETSTAT_LISTENING)
+ printf("(only servers)");
+ else
+ printf("(w/o servers)");
+ printf(net_conn_line_header, "Local Address", "Foreign Address");
+ print_progname_banner();
+ bb_putchar('\n');
}
- if (inet && flags&NETSTAT_TCP)
- do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
-#ifdef CONFIG_FEATURE_IPV6
- if (inet6 && flags&NETSTAT_TCP)
- do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one);
+ if (flags & NETSTAT_TCP) {
+ do_info("/proc/net/tcp", tcp_do_one);
+#if ENABLE_FEATURE_IPV6
+ do_info("/proc/net/tcp6", tcp_do_one);
#endif
- if (inet && flags&NETSTAT_UDP)
- do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
-#ifdef CONFIG_FEATURE_IPV6
- if (inet6 && flags&NETSTAT_UDP)
- do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one);
+ }
+ if (flags & NETSTAT_UDP) {
+ do_info("/proc/net/udp", udp_do_one);
+#if ENABLE_FEATURE_IPV6
+ do_info("/proc/net/udp6", udp_do_one);
#endif
- if (inet && flags&NETSTAT_RAW)
- do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
-#ifdef CONFIG_FEATURE_IPV6
- if (inet6 && flags&NETSTAT_RAW)
- do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one);
+ }
+ if (flags & NETSTAT_RAW) {
+ do_info("/proc/net/raw", raw_do_one);
+#if ENABLE_FEATURE_IPV6
+ do_info("/proc/net/raw6", raw_do_one);
#endif
- if (flags&NETSTAT_UNIX) {
+ }
+ if (flags & NETSTAT_UNIX) {
printf("Active UNIX domain sockets ");
- if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
+ if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
printf("(servers and established)");
- else {
- if (flags&NETSTAT_LISTENING)
- printf("(only servers)");
- else
- printf("(w/o servers)");
- }
-
- printf("\nProto RefCnt Flags Type State I-Node Path\n");
- do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
+ else if (flags & NETSTAT_LISTENING)
+ printf("(only servers)");
+ else
+ printf("(w/o servers)");
+ printf("\nProto RefCnt Flags Type State I-Node ");
+ print_progname_banner();
+ printf("Path\n");
+ do_info("/proc/net/unix", unix_do_one);
}
+ prg_cache_clear();
return 0;
}
diff --git a/release/src/router/busybox/networking/nslookup.c b/release/src/router/busybox/networking/nslookup.c
index 307f6104..26287114 100644
--- a/release/src/router/busybox/networking/nslookup.c
+++ b/release/src/router/busybox/networking/nslookup.c
@@ -5,198 +5,171 @@
* Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
* Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
*
- * Correct default name server display and explicit name server option
+ * Correct default name server display and explicit name server option
* added by Ben Zeckel <bzeckel@hmc.edu> June 2001
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <netdb.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <netinet/in.h>
#include <resolv.h>
-#include <arpa/inet.h>
-#include "busybox.h"
+#include "libbb.h"
/*
- | I'm only implementing non-interactive mode;
- | I totally forgot nslookup even had an interactive mode.
+ * I'm only implementing non-interactive mode;
+ * I totally forgot nslookup even had an interactive mode.
+ *
+ * This applet is the only user of res_init(). Without it,
+ * you may avoid pulling in _res global from libc.
*/
-/* only works for IPv4 */
-static int addr_fprint(char *addr)
-{
- u_int8_t split[4];
- u_int32_t ip;
- u_int32_t *x = (u_int32_t *) addr;
-
- ip = ntohl(*x);
- split[0] = (ip & 0xff000000) >> 24;
- split[1] = (ip & 0x00ff0000) >> 16;
- split[2] = (ip & 0x0000ff00) >> 8;
- split[3] = (ip & 0x000000ff);
- printf("%d.%d.%d.%d", split[0], split[1], split[2], split[3]);
- return 0;
-}
-
-/* takes the NULL-terminated array h_addr_list, and
- * prints its contents appropriately
+/* Examples of 'standard' nslookup output
+ * $ nslookup yahoo.com
+ * Server: 128.193.0.10
+ * Address: 128.193.0.10#53
+ *
+ * Non-authoritative answer:
+ * Name: yahoo.com
+ * Address: 216.109.112.135
+ * Name: yahoo.com
+ * Address: 66.94.234.13
+ *
+ * $ nslookup 204.152.191.37
+ * Server: 128.193.4.20
+ * Address: 128.193.4.20#53
+ *
+ * Non-authoritative answer:
+ * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa.
+ * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org.
+ *
+ * Authoritative answers can be found from:
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org.
+ * ns1.kernel.org internet address = 140.211.167.34
+ * ns2.kernel.org internet address = 204.152.191.4
+ * ns3.kernel.org internet address = 204.152.191.36
*/
-static int addr_list_fprint(char **h_addr_list)
+
+static int print_host(const char *hostname, const char *header)
{
- int i, j;
- char *addr_string = (h_addr_list[1])
- ? "Addresses: " : "Address: ";
-
- printf("%s ", addr_string);
- for (i = 0, j = 0; h_addr_list[i]; i++, j++) {
- addr_fprint(h_addr_list[i]);
-
- /* real nslookup does this */
- if (j == 4) {
- if (h_addr_list[i + 1]) {
- printf("\n ");
- }
- j = 0;
- } else {
- if (h_addr_list[i + 1]) {
- printf(", ");
+ /* We can't use xhost2sockaddr() - we want to get ALL addresses,
+ * not just one */
+ struct addrinfo *result = NULL;
+ int rc;
+ struct addrinfo hint;
+
+ memset(&hint, 0 , sizeof(hint));
+ /* hint.ai_family = AF_UNSPEC; - zero anyway */
+ /* Needed. Or else we will get each address thrice (or more)
+ * for each possible socket type (tcp,udp,raw...): */
+ hint.ai_socktype = SOCK_STREAM;
+ // hint.ai_flags = AI_CANONNAME;
+ rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
+
+ if (!rc) {
+ struct addrinfo *cur = result;
+ unsigned cnt = 0;
+
+ printf("%-10s %s\n", header, hostname);
+ // puts(cur->ai_canonname); ?
+ while (cur) {
+ char *dotted, *revhost;
+ dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr);
+ revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr);
+
+ printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n');
+ if (revhost) {
+ puts(revhost);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(revhost);
}
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(dotted);
+ cur = cur->ai_next;
}
-
- }
- printf("\n");
- return 0;
-}
-
-/* print the results as nslookup would */
-static struct hostent *hostent_fprint(struct hostent *host, const char *server_host)
-{
- if (host) {
- printf("%s %s\n", server_host, host->h_name);
- addr_list_fprint(host->h_addr_list);
} else {
- printf("*** Unknown host\n");
+#if ENABLE_VERBOSE_RESOLUTION_ERRORS
+ bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
+#else
+ bb_error_msg("can't resolve '%s'", hostname);
+#endif
}
- return host;
-}
-
-/* changes a c-string matching the perl regex \d+\.\d+\.\d+\.\d+
- * into a u_int32_t
- */
-static u_int32_t str_to_addr(const char *addr)
-{
- u_int32_t split[4];
- u_int32_t ip;
-
- sscanf(addr, "%d.%d.%d.%d",
- &split[0], &split[1], &split[2], &split[3]);
-
- /* assuming sscanf worked */
- ip = (split[0] << 24) |
- (split[1] << 16) | (split[2] << 8) | (split[3]);
-
- return htonl(ip);
-}
-
-/* gethostbyaddr wrapper */
-static struct hostent *gethostbyaddr_wrapper(const char *address)
-{
- struct in_addr addr;
-
- addr.s_addr = str_to_addr(address);
- return gethostbyaddr((char *) &addr, 4, AF_INET); /* IPv4 only for now */
+ if (ENABLE_FEATURE_CLEAN_UP)
+ freeaddrinfo(result);
+ return (rc != 0);
}
/* lookup the default nameserver and display it */
-static inline void server_print(void)
+static void server_print(void)
{
- struct sockaddr_in def = _res.nsaddr_list[0];
- char *ip = inet_ntoa(def.sin_addr);
-
- hostent_fprint(gethostbyaddr_wrapper(ip), "Server:");
- printf("\n");
+ char *server;
+ struct sockaddr *sa;
+
+#if ENABLE_FEATURE_IPV6
+ sa = (struct sockaddr*)_res._u._ext.nsaddrs[0];
+ if (!sa)
+#endif
+ sa = (struct sockaddr*)&_res.nsaddr_list[0];
+ server = xmalloc_sockaddr2dotted_noport(sa);
+
+ print_host(server, "Server:");
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(server);
+ bb_putchar('\n');
}
/* alter the global _res nameserver structure to use
- an explicit dns server instead of what is in /etc/resolv.h */
-static inline void set_default_dns(char *server)
+ an explicit dns server instead of what is in /etc/resolv.conf */
+static void set_default_dns(const char *server)
{
- struct in_addr server_in_addr;
+ len_and_sockaddr *lsa;
- if(inet_aton(server,&server_in_addr))
- {
+ /* NB: this works even with, say, "[::1]:5353"! :) */
+ lsa = xhost2sockaddr(server, 53);
+
+ if (lsa->u.sa.sa_family == AF_INET) {
_res.nscount = 1;
- _res.nsaddr_list[0].sin_addr = server_in_addr;
+ /* struct copy */
+ _res.nsaddr_list[0] = lsa->u.sin;
}
-}
-
-/* naive function to check whether char *s is an ip address */
-static int is_ip_address(const char *s)
-{
- while (*s) {
- if ((isdigit(*s)) || (*s == '.')) {
- s++;
- continue;
- }
- return 0;
+#if ENABLE_FEATURE_IPV6
+ /* Hoped libc can cope with IPv4 address there too.
+ * No such luck, glibc 2.4 segfaults even with IPv6,
+ * maybe I misunderstand how to make glibc use IPv6 addr?
+ * (uclibc 0.9.31+ should work) */
+ if (lsa->u.sa.sa_family == AF_INET6) {
+ // glibc neither SEGVs nor sends any dgrams with this
+ // (strace shows no socket ops):
+ //_res.nscount = 0;
+ _res._u._ext.nscount = 1;
+ /* store a pointer to part of malloc'ed lsa */
+ _res._u._ext.nsaddrs[0] = &lsa->u.sin6;
+ /* must not free(lsa)! */
}
- return 1;
+#endif
}
-/* ________________________________________________________________________ */
+int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int nslookup_main(int argc, char **argv)
{
- struct hostent *host;
+ /* We allow 1 or 2 arguments.
+ * The first is the name to be looked up and the second is an
+ * optional DNS server with which to do the lookup.
+ * More than 3 arguments is an error to follow the pattern of the
+ * standard nslookup */
+ if (!argv[1] || argv[1][0] == '-' || argc > 3)
+ bb_show_usage();
- /*
- * initialize DNS structure _res used in printing the default
- * name server and in the explicit name server option feature.
- */
-
+ /* initialize DNS structure _res used in printing the default
+ * name server and in the explicit name server option feature. */
res_init();
+ /* rfc2133 says this enables IPv6 lookups */
+ /* (but it also says "may be enabled in /etc/resolv.conf") */
+ /*_res.options |= RES_USE_INET6;*/
- /*
- * We allow 1 or 2 arguments.
- * The first is the name to be looked up and the second is an
- * optional DNS server with which to do the lookup.
- * More than 3 arguments is an error to follow the pattern of the
- * standard nslookup
- */
-
- if (argc < 2 || *argv[1]=='-' || argc > 3)
- bb_show_usage();
- else if(argc == 3)
+ if (argv[2])
set_default_dns(argv[2]);
server_print();
- if (is_ip_address(argv[1])) {
- host = gethostbyaddr_wrapper(argv[1]);
- } else {
- host = xgethostbyname(argv[1]);
- }
- hostent_fprint(host, "Name: ");
- return EXIT_SUCCESS;
+ return print_host(argv[1], "Name:");
}
-
-/* $Id: nslookup.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $ */
diff --git a/release/src/router/busybox/networking/ping.c b/release/src/router/busybox/networking/ping.c
index dbc95d6a..f2a612fd 100644
--- a/release/src/router/busybox/networking/ping.c
+++ b/release/src/router/busybox/networking/ping.c
@@ -1,76 +1,56 @@
/* vi: set sw=4 ts=4: */
/*
- * $Id: ping.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
* Mini ping implementation for busybox
*
* Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Adapted from the ping in netkit-base 0.10:
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+/* from ping6.c:
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
*
* This version of ping is adapted from the ping in netkit-base 0.10,
* which is:
*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Mike Muuss.
- *
* Original copyright notice is retained at the end of this file.
+ *
+ * This version is an adaptation of ping.c from busybox.
+ * The code was modified by Bart Visscher <magick@linux-fan.com>
*/
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/file.h>
-#include <sys/time.h>
-#include <sys/times.h>
-#include <sys/signal.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
+#include <net/if.h>
#include <netinet/ip_icmp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include "busybox.h"
-
-
-static const int DEFDATALEN = 56;
-static const int MAXIPLEN = 60;
-static const int MAXICMPLEN = 76;
-static const int MAXPACKET = 65468;
-#define MAX_DUP_CHK (8 * 128)
-static const int MAXWAIT = 10;
-static const int PINGINTERVAL = 1; /* second */
-
-#define O_QUIET (1 << 0)
-
-#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
-#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
-#define SET(bit) (A(bit) |= B(bit))
-#define CLR(bit) (A(bit) &= (~B(bit)))
-#define TST(bit) (A(bit) & B(bit))
-
-static void ping(const char *host);
+#include "libbb.h"
+
+#if ENABLE_PING6
+#include <netinet/icmp6.h>
+/* I see RENUMBERED constants in bits/in.h - !!?
+ * What a fuck is going on with libc? Is it a glibc joke? */
+#ifdef IPV6_2292HOPLIMIT
+#undef IPV6_HOPLIMIT
+#define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
+#endif
+#endif
+
+enum {
+ DEFDATALEN = 56,
+ MAXIPLEN = 60,
+ MAXICMPLEN = 76,
+ MAXPACKET = 65468,
+ MAX_DUP_CHK = (8 * 128),
+ MAXWAIT = 10,
+ PINGINTERVAL = 1, /* 1 second */
+};
/* common routines */
+
static int in_cksum(unsigned short *buf, int sz)
{
int nleft = sz;
@@ -91,58 +71,49 @@ static int in_cksum(unsigned short *buf, int sz)
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
ans = ~sum;
- return (ans);
+ return ans;
}
+#if !ENABLE_FEATURE_FANCY_PING
+
/* simple version */
-#ifndef CONFIG_FEATURE_FANCY_PING
-static char *hostname = NULL;
-static void noresp(int ign)
+
+static char *hostname;
+
+static void noresp(int ign UNUSED_PARAM)
{
printf("No response from %s\n", hostname);
exit(EXIT_FAILURE);
}
-static void ping(const char *host)
+static void ping4(len_and_sockaddr *lsa)
{
- struct hostent *h;
struct sockaddr_in pingaddr;
struct icmp *pkt;
int pingsock, c;
char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
pingsock = create_icmp_socket();
-
- memset(&pingaddr, 0, sizeof(struct sockaddr_in));
-
- pingaddr.sin_family = AF_INET;
- h = xgethostbyname(host);
- memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
- hostname = h->h_name;
+ pingaddr = lsa->u.sin;
pkt = (struct icmp *) packet;
memset(pkt, 0, sizeof(packet));
pkt->icmp_type = ICMP_ECHO;
pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
- c = sendto(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
-
- if (c < 0 || c != sizeof(packet))
- bb_perror_msg_and_die("sendto");
+ c = xsendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN,
+ (struct sockaddr *) &pingaddr, sizeof(pingaddr));
- signal(SIGALRM, noresp);
- alarm(5); /* give the host 5000ms to respond */
/* listen for replies */
while (1) {
struct sockaddr_in from;
- size_t fromlen = sizeof(from);
+ socklen_t fromlen = sizeof(from);
- if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &from, &fromlen)) < 0) {
- if (errno == EINTR)
- continue;
- bb_perror_msg("recvfrom");
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
continue;
}
if (c >= 76) { /* ip + icmp */
@@ -153,289 +124,658 @@ static void ping(const char *host)
break;
}
}
- printf("%s is alive!\n", hostname);
- return;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
}
-extern int ping_main(int argc, char **argv)
+#if ENABLE_PING6
+static void ping6(len_and_sockaddr *lsa)
{
- argc--;
+ struct sockaddr_in6 pingaddr;
+ struct icmp6_hdr *pkt;
+ int pingsock, c;
+ int sockopt;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp6_socket();
+ pingaddr = lsa->u.sin6;
+
+ pkt = (struct icmp6_hdr *) packet;
+ memset(pkt, 0, sizeof(packet));
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+
+ c = xsendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr),
+ (struct sockaddr *) &pingaddr, sizeof(pingaddr));
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in6 from;
+ socklen_t fromlen = sizeof(from);
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ if (c >= 8) { /* icmp6_hdr */
+ pkt = (struct icmp6_hdr *) packet;
+ if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
+ break;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+}
+#endif
+
+int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ping_main(int argc UNUSED_PARAM, char **argv)
+{
+ len_and_sockaddr *lsa;
+#if ENABLE_PING6
+ sa_family_t af = AF_UNSPEC;
+
+ while ((++argv)[0] && argv[0][0] == '-') {
+ if (argv[0][1] == '4') {
+ af = AF_INET;
+ continue;
+ }
+ if (argv[0][1] == '6') {
+ af = AF_INET6;
+ continue;
+ }
+ bb_show_usage();
+ }
+#else
argv++;
- if (argc < 1)
+#endif
+
+ hostname = *argv;
+ if (!hostname)
bb_show_usage();
- ping(*argv);
+
+#if ENABLE_PING6
+ lsa = xhost_and_af2sockaddr(hostname, 0, af);
+#else
+ lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+ /* Set timer _after_ DNS resolution */
+ signal(SIGALRM, noresp);
+ alarm(5); /* give the host 5000ms to respond */
+
+#if ENABLE_PING6
+ if (lsa->u.sa.sa_family == AF_INET6)
+ ping6(lsa);
+ else
+#endif
+ ping4(lsa);
+ printf("%s is alive!\n", hostname);
return EXIT_SUCCESS;
}
-#else /* ! CONFIG_FEATURE_FANCY_PING */
+
+#else /* FEATURE_FANCY_PING */
+
+
/* full(er) version */
-static struct sockaddr_in pingaddr;
-static int pingsock = -1;
-static int datalen; /* intentionally uninitialized to work around gcc bug */
-static long ntransmitted, nreceived, nrepeats, pingcount;
-static int myid, options;
-static unsigned long tmin = ULONG_MAX, tmax, tsum;
-static char rcvd_tbl[MAX_DUP_CHK / 8];
+#define OPT_STRING ("qvc:s:w:W:I:4" USE_PING6("6"))
+enum {
+ OPT_QUIET = 1 << 0,
+ OPT_VERBOSE = 1 << 1,
+ OPT_c = 1 << 2,
+ OPT_s = 1 << 3,
+ OPT_w = 1 << 4,
+ OPT_W = 1 << 5,
+ OPT_I = 1 << 6,
+ OPT_IPV4 = 1 << 7,
+ OPT_IPV6 = (1 << 8) * ENABLE_PING6,
+};
+
+
+struct globals {
+ int pingsock;
+ int if_index;
+ char *str_I;
+ len_and_sockaddr *source_lsa;
+ unsigned datalen;
+ unsigned pingcount; /* must be int-sized */
+ unsigned long ntransmitted, nreceived, nrepeats;
+ uint16_t myid;
+ unsigned tmin, tmax; /* in us */
+ unsigned long long tsum; /* in us, sum of all times */
+ unsigned deadline;
+ unsigned timeout;
+ unsigned total_secs;
+ const char *hostname;
+ const char *dotted;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if ENABLE_PING6
+ struct sockaddr_in6 sin6;
+#endif
+ } pingaddr;
+ char rcvd_tbl[MAX_DUP_CHK / 8];
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define pingsock (G.pingsock )
+#define if_index (G.if_index )
+#define source_lsa (G.source_lsa )
+#define str_I (G.str_I )
+#define datalen (G.datalen )
+#define ntransmitted (G.ntransmitted)
+#define nreceived (G.nreceived )
+#define nrepeats (G.nrepeats )
+#define pingcount (G.pingcount )
+#define myid (G.myid )
+#define tmin (G.tmin )
+#define tmax (G.tmax )
+#define tsum (G.tsum )
+#define deadline (G.deadline )
+#define timeout (G.timeout )
+#define total_secs (G.total_secs )
+#define hostname (G.hostname )
+#define dotted (G.dotted )
+#define pingaddr (G.pingaddr )
+#define rcvd_tbl (G.rcvd_tbl )
+void BUG_ping_globals_too_big(void);
+#define INIT_G() do { \
+ if (sizeof(G) > COMMON_BUFSIZE) \
+ BUG_ping_globals_too_big(); \
+ pingsock = -1; \
+ datalen = DEFDATALEN; \
+ timeout = MAXWAIT; \
+ tmin = UINT_MAX; \
+} while (0)
-struct hostent *hostent;
-static void sendping(int);
-static void pingstats(int);
-static void unpack(char *, int, struct sockaddr_in *);
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
/**************************************************************************/
-static void pingstats(int junk)
+static void print_stats_and_exit(int junk) NORETURN;
+static void print_stats_and_exit(int junk UNUSED_PARAM)
{
- int status;
-
signal(SIGINT, SIG_IGN);
- printf("\n--- %s ping statistics ---\n", hostent->h_name);
- printf("%ld packets transmitted, ", ntransmitted);
- printf("%ld packets received, ", nreceived);
+ printf("\n--- %s ping statistics ---\n", hostname);
+ printf("%lu packets transmitted, ", ntransmitted);
+ printf("%lu packets received, ", nreceived);
if (nrepeats)
- printf("%ld duplicates, ", nrepeats);
+ printf("%lu duplicates, ", nrepeats);
if (ntransmitted)
- printf("%ld%% packet loss\n",
- (ntransmitted - nreceived) * 100 / ntransmitted);
- if (nreceived)
- printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
- tmin / 10, tmin % 10,
- (tsum / (nreceived + nrepeats)) / 10,
- (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
- if (nreceived != 0)
- status = EXIT_SUCCESS;
- else
- status = EXIT_FAILURE;
- exit(status);
+ ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
+ printf("%lu%% packet loss\n", ntransmitted);
+ if (tmin != UINT_MAX) {
+ unsigned tavg = tsum / (nreceived + nrepeats);
+ printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
+ tmin / 1000, tmin % 1000,
+ tavg / 1000, tavg % 1000,
+ tmax / 1000, tmax % 1000);
+ }
+ /* if condition is true, exit with 1 -- 'failure' */
+ exit(nreceived == 0 || (deadline && nreceived < pingcount));
}
-static void sendping(int junk)
+static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
{
- struct icmp *pkt;
- int i;
- char packet[datalen + 8];
+ int sz;
+
+ CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
+ ntransmitted++;
+
+ /* sizeof(pingaddr) can be larger than real sa size, but I think
+ * it doesn't matter */
+ sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr));
+ if (sz != size_pkt)
+ bb_error_msg_and_die(bb_msg_write_error);
+
+ if (pingcount == 0 || deadline || ntransmitted < pingcount) {
+ /* Didn't send all pings yet - schedule next in 1s */
+ signal(SIGALRM, sp);
+ if (deadline) {
+ total_secs += PINGINTERVAL;
+ if (total_secs >= deadline)
+ signal(SIGALRM, print_stats_and_exit);
+ }
+ alarm(PINGINTERVAL);
+ } else { /* -c NN, and all NN are sent (and no deadline) */
+ /* Wait for the last ping to come back.
+ * -W timeout: wait for a response in seconds.
+ * Affects only timeout in absense of any responses,
+ * otherwise ping waits for two RTTs. */
+ unsigned expire = timeout;
+
+ if (nreceived) {
+ /* approx. 2*tmax, in seconds (2 RTT) */
+ expire = tmax / (512*1024);
+ if (expire == 0)
+ expire = 1;
+ }
+ signal(SIGALRM, print_stats_and_exit);
+ alarm(expire);
+ }
+}
- pkt = (struct icmp *) packet;
+static void sendping4(int junk UNUSED_PARAM)
+{
+ /* +4 reserves a place for timestamp, which may end up sitting
+ * *after* packet. Saves one if() */
+ struct icmp *pkt = alloca(datalen + ICMP_MINLEN + 4);
+ memset(pkt, 0, datalen + ICMP_MINLEN + 4);
pkt->icmp_type = ICMP_ECHO;
- pkt->icmp_code = 0;
- pkt->icmp_cksum = 0;
- pkt->icmp_seq = ntransmitted++;
+ /*pkt->icmp_code = 0;*/
+ /*pkt->icmp_cksum = 0;*/
+ pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
pkt->icmp_id = myid;
- CLR(pkt->icmp_seq % MAX_DUP_CHK);
- gettimeofday((struct timeval *) &packet[8], NULL);
- pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
+ /* We don't do hton, because we will read it back on the same machine */
+ /*if (datalen >= 4)*/
+ *(uint32_t*)&pkt->icmp_dun = monotonic_us();
+
+ pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
- i = sendto(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
+ sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
+}
+#if ENABLE_PING6
+static void sendping6(int junk UNUSED_PARAM)
+{
+ struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4);
- if (i < 0)
- bb_perror_msg_and_die("sendto");
- else if ((size_t)i != sizeof(packet))
- bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
- (int)sizeof(packet));
+ memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4);
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+ /*pkt->icmp6_code = 0;*/
+ /*pkt->icmp6_cksum = 0;*/
+ pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+ pkt->icmp6_id = myid;
- signal(SIGALRM, sendping);
- if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
- alarm(PINGINTERVAL);
- } else { /* done, wait for the last ping to come back */
- /* todo, don't necessarily need to wait so long... */
- signal(SIGALRM, pingstats);
- alarm(MAXWAIT);
- }
+ /*if (datalen >= 4)*/
+ *(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
+
+ sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
}
+#endif
-static char *icmp_type_name (int id)
+static const char *icmp_type_name(int id)
{
switch (id) {
- case ICMP_ECHOREPLY: return "Echo Reply";
- case ICMP_DEST_UNREACH: return "Destination Unreachable";
- case ICMP_SOURCE_QUENCH: return "Source Quench";
- case ICMP_REDIRECT: return "Redirect (change route)";
- case ICMP_ECHO: return "Echo Request";
- case ICMP_TIME_EXCEEDED: return "Time Exceeded";
- case ICMP_PARAMETERPROB: return "Parameter Problem";
- case ICMP_TIMESTAMP: return "Timestamp Request";
- case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
- case ICMP_INFO_REQUEST: return "Information Request";
- case ICMP_INFO_REPLY: return "Information Reply";
- case ICMP_ADDRESS: return "Address Mask Request";
- case ICMP_ADDRESSREPLY: return "Address Mask Reply";
- default: return "unknown ICMP type";
+ case ICMP_ECHOREPLY: return "Echo Reply";
+ case ICMP_DEST_UNREACH: return "Destination Unreachable";
+ case ICMP_SOURCE_QUENCH: return "Source Quench";
+ case ICMP_REDIRECT: return "Redirect (change route)";
+ case ICMP_ECHO: return "Echo Request";
+ case ICMP_TIME_EXCEEDED: return "Time Exceeded";
+ case ICMP_PARAMETERPROB: return "Parameter Problem";
+ case ICMP_TIMESTAMP: return "Timestamp Request";
+ case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
+ case ICMP_INFO_REQUEST: return "Information Request";
+ case ICMP_INFO_REPLY: return "Information Reply";
+ case ICMP_ADDRESS: return "Address Mask Request";
+ case ICMP_ADDRESSREPLY: return "Address Mask Reply";
+ default: return "unknown ICMP type";
}
}
+#if ENABLE_PING6
+/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
+ * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
+#ifndef MLD_LISTENER_QUERY
+# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
+#endif
+#ifndef MLD_LISTENER_REPORT
+# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
+#endif
+#ifndef MLD_LISTENER_REDUCTION
+# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
+#endif
+static const char *icmp6_type_name(int id)
+{
+ switch (id) {
+ case ICMP6_DST_UNREACH: return "Destination Unreachable";
+ case ICMP6_PACKET_TOO_BIG: return "Packet too big";
+ case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
+ case ICMP6_PARAM_PROB: return "Parameter Problem";
+ case ICMP6_ECHO_REPLY: return "Echo Reply";
+ case ICMP6_ECHO_REQUEST: return "Echo Request";
+ case MLD_LISTENER_QUERY: return "Listener Query";
+ case MLD_LISTENER_REPORT: return "Listener Report";
+ case MLD_LISTENER_REDUCTION: return "Listener Reduction";
+ default: return "unknown ICMP type";
+ }
+}
+#endif
+
+static void unpack_tail(int sz, uint32_t *tp,
+ const char *from_str,
+ uint16_t recv_seq, int ttl)
+{
+ const char *dupmsg = " (DUP!)";
+ unsigned triptime = triptime; /* for gcc */
+
+ ++nreceived;
+
+ if (tp) {
+ /* (int32_t) cast is for hypothetical 64-bit unsigned */
+ /* (doesn't hurt 32-bit real-world anyway) */
+ triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
-static void unpack(char *buf, int sz, struct sockaddr_in *from)
+ if (TST(recv_seq % MAX_DUP_CHK)) {
+ ++nrepeats;
+ --nreceived;
+ } else {
+ SET(recv_seq % MAX_DUP_CHK);
+ dupmsg += 7;
+ }
+
+ if (option_mask32 & OPT_QUIET)
+ return;
+
+ printf("%d bytes from %s: seq=%u ttl=%d", sz,
+ from_str, recv_seq, ttl);
+ if (tp)
+ printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
+ puts(dupmsg);
+ fflush(stdout);
+}
+static void unpack4(char *buf, int sz, struct sockaddr_in *from)
{
struct icmp *icmppkt;
struct iphdr *iphdr;
- struct timeval tv, *tp;
- int hlen, dupflag;
- unsigned long triptime;
-
- gettimeofday(&tv, NULL);
+ int hlen;
- /* check IP header */
- iphdr = (struct iphdr *) buf;
- hlen = iphdr->ihl << 2;
/* discard if too short */
if (sz < (datalen + ICMP_MINLEN))
return;
+ /* check IP header */
+ iphdr = (struct iphdr *) buf;
+ hlen = iphdr->ihl << 2;
sz -= hlen;
icmppkt = (struct icmp *) (buf + hlen);
-
if (icmppkt->icmp_id != myid)
- return; /* not our ping */
+ return; /* not our ping */
if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
- ++nreceived;
- tp = (struct timeval *) icmppkt->icmp_data;
-
- if ((tv.tv_usec -= tp->tv_usec) < 0) {
- --tv.tv_sec;
- tv.tv_usec += 1000000;
- }
- tv.tv_sec -= tp->tv_sec;
-
- triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
- tsum += triptime;
- if (triptime < tmin)
- tmin = triptime;
- if (triptime > tmax)
- tmax = triptime;
+ uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
+ uint32_t *tp = NULL;
+
+ if (sz >= ICMP_MINLEN + sizeof(uint32_t))
+ tp = (uint32_t *) icmppkt->icmp_data;
+ unpack_tail(sz, tp,
+ inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
+ recv_seq, iphdr->ttl);
+ } else if (icmppkt->icmp_type != ICMP_ECHO) {
+ bb_error_msg("warning: got ICMP %d (%s)",
+ icmppkt->icmp_type,
+ icmp_type_name(icmppkt->icmp_type));
+ }
+}
+#if ENABLE_PING6
+static void unpack6(char *packet, int sz, /*struct sockaddr_in6 *from,*/ int hoplimit)
+{
+ struct icmp6_hdr *icmppkt;
+ char buf[INET6_ADDRSTRLEN];
- if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
- ++nrepeats;
- --nreceived;
- dupflag = 1;
- } else {
- SET(icmppkt->icmp_seq % MAX_DUP_CHK);
- dupflag = 0;
- }
+ /* discard if too short */
+ if (sz < (datalen + sizeof(struct icmp6_hdr)))
+ return;
- if (options & O_QUIET)
- return;
-
- printf("%d bytes from %s: icmp_seq=%u", sz,
- inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
- icmppkt->icmp_seq);
- printf(" ttl=%d", iphdr->ttl);
- printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
- if (dupflag)
- printf(" (DUP!)");
- printf("\n");
- } else
- if (icmppkt->icmp_type != ICMP_ECHO)
- bb_error_msg("Warning: Got ICMP %d (%s)",
- icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
+ icmppkt = (struct icmp6_hdr *) packet;
+ if (icmppkt->icmp6_id != myid)
+ return; /* not our ping */
+
+ if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
+ uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
+ uint32_t *tp = NULL;
+
+ if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
+ tp = (uint32_t *) &icmppkt->icmp6_data8[4];
+ unpack_tail(sz, tp,
+ inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
+ buf, sizeof(buf)),
+ recv_seq, hoplimit);
+ } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
+ bb_error_msg("warning: got ICMP %d (%s)",
+ icmppkt->icmp6_type,
+ icmp6_type_name(icmppkt->icmp6_type));
+ }
}
+#endif
-static void ping(const char *host)
+static void ping4(len_and_sockaddr *lsa)
{
char packet[datalen + MAXIPLEN + MAXICMPLEN];
int sockopt;
pingsock = create_icmp_socket();
+ pingaddr.sin = lsa->u.sin;
+ if (source_lsa) {
+ if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
+ &source_lsa->u.sa, source_lsa->len))
+ bb_error_msg_and_die("can't set multicast source interface");
+ xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
+ }
+ if (str_I)
+ setsockopt_bindtodevice(pingsock, str_I);
+
+ /* enable broadcast pings */
+ setsockopt_broadcast(pingsock);
- memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+ /* set recv buf (needed if we can get lots of responses: flood ping,
+ * broadcast ping etc) */
+ sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+
+ signal(SIGINT, print_stats_and_exit);
+
+ /* start the ping's going ... */
+ sendping4(0);
- pingaddr.sin_family = AF_INET;
- hostent = xgethostbyname(host);
- if (hostent->h_addrtype != AF_INET)
- bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ socklen_t fromlen = (socklen_t) sizeof(from);
+ int c;
- memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ unpack4(packet, c, &from);
+ if (pingcount && nreceived >= pingcount)
+ break;
+ }
+}
+#if ENABLE_PING6
+extern int BUG_bad_offsetof_icmp6_cksum(void);
+static void ping6(len_and_sockaddr *lsa)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ int sockopt;
+ struct msghdr msg;
+ struct sockaddr_in6 from;
+ struct iovec iov;
+ char control_buf[CMSG_SPACE(36)];
+
+ pingsock = create_icmp6_socket();
+ pingaddr.sin6 = lsa->u.sin6;
+ /* untested whether "-I addr" really works for IPv6: */
+ if (source_lsa)
+ xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
+ if (str_I)
+ setsockopt_bindtodevice(pingsock, str_I);
+
+#ifdef ICMP6_FILTER
+ {
+ struct icmp6_filter filt;
+ if (!(option_mask32 & OPT_VERBOSE)) {
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+ } else {
+ ICMP6_FILTER_SETPASSALL(&filt);
+ }
+ if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
+ }
+#endif /*ICMP6_FILTER*/
/* enable broadcast pings */
- sockopt = 1;
- setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
- sizeof(sockopt));
+ setsockopt_broadcast(pingsock);
- /* set recv buf for broadcast pings */
- sockopt = 48 * 1024;
- setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
- sizeof(sockopt));
+ /* set recv buf (needed if we can get lots of responses: flood ping,
+ * broadcast ping etc) */
+ sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
- printf("PING %s (%s): %d data bytes\n",
- hostent->h_name,
- inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
- datalen);
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
+ BUG_bad_offsetof_icmp6_cksum();
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
- signal(SIGINT, pingstats);
+ /* request ttl info to be returned in ancillary data */
+ setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
+
+ if (if_index)
+ pingaddr.sin6.sin6_scope_id = if_index;
+
+ signal(SIGINT, print_stats_and_exit);
/* start the ping's going ... */
- sendping(0);
+ sendping6(0);
/* listen for replies */
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control_buf;
+ iov.iov_base = packet;
+ iov.iov_len = sizeof(packet);
while (1) {
- struct sockaddr_in from;
- socklen_t fromlen = (socklen_t) sizeof(from);
int c;
-
- if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &from, &fromlen)) < 0) {
- if (errno == EINTR)
- continue;
- bb_perror_msg("recvfrom");
+ struct cmsghdr *mp;
+ int hoplimit = -1;
+ msg.msg_controllen = sizeof(control_buf);
+
+ c = recvmsg(pingsock, &msg, 0);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
continue;
}
- unpack(packet, c, &from);
- if (pingcount > 0 && nreceived >= pingcount)
+ for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
+ if (mp->cmsg_level == SOL_IPV6
+ && mp->cmsg_type == IPV6_HOPLIMIT
+ /* don't check len - we trust the kernel: */
+ /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
+ ) {
+ hoplimit = *(int*)CMSG_DATA(mp);
+ }
+ }
+ unpack6(packet, c, /*&from,*/ hoplimit);
+ if (pingcount && nreceived >= pingcount)
break;
}
- pingstats(0);
}
+#endif
-extern int ping_main(int argc, char **argv)
+static void ping(len_and_sockaddr *lsa)
{
- char *thisarg;
+ printf("PING %s (%s)", hostname, dotted);
+ if (source_lsa) {
+ printf(" from %s",
+ xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa));
+ }
+ printf(": %d data bytes\n", datalen);
- datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+#if ENABLE_PING6
+ if (lsa->u.sa.sa_family == AF_INET6)
+ ping6(lsa);
+ else
+#endif
+ ping4(lsa);
+}
- argc--;
- argv++;
- options = 0;
- /* Parse any options */
- while (argc >= 1 && **argv == '-') {
- thisarg = *argv;
- thisarg++;
- switch (*thisarg) {
- case 'q':
- options |= O_QUIET;
- break;
- case 'c':
- if (--argc <= 0)
- bb_show_usage();
- argv++;
- pingcount = atoi(*argv);
- break;
- case 's':
- if (--argc <= 0)
- bb_show_usage();
- argv++;
- datalen = atoi(*argv);
- break;
- default:
- bb_show_usage();
+int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ping_main(int argc UNUSED_PARAM, char **argv)
+{
+ len_and_sockaddr *lsa;
+ char *str_s;
+ int opt;
+
+ INIT_G();
+
+ /* exactly one argument needed; -v and -q don't mix; -c NUM, -w NUM, -W NUM */
+ opt_complementary = "=1:q--v:v--q:c+:w+:W+";
+ opt = getopt32(argv, OPT_STRING, &pingcount, &str_s, &deadline, &timeout, &str_I);
+ if (opt & OPT_s)
+ datalen = xatou16(str_s); // -s
+ if (opt & OPT_I) { // -I
+ if_index = if_nametoindex(str_I);
+ if (!if_index) {
+ /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
+ source_lsa = xdotted2sockaddr(str_I, 0);
+ str_I = NULL; /* don't try to bind to device later */
}
- argc--;
- argv++;
}
- if (argc < 1)
- bb_show_usage();
+ myid = (uint16_t) getpid();
+ hostname = argv[optind];
+#if ENABLE_PING6
+ {
+ sa_family_t af = AF_UNSPEC;
+ if (opt & OPT_IPV4)
+ af = AF_INET;
+ if (opt & OPT_IPV6)
+ af = AF_INET6;
+ lsa = xhost_and_af2sockaddr(hostname, 0, af);
+ }
+#else
+ lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+
+ if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family)
+ /* leaking it here... */
+ source_lsa = NULL;
+
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+ ping(lsa);
+ print_stats_and_exit(EXIT_SUCCESS);
+ /*return EXIT_SUCCESS;*/
+}
+#endif /* FEATURE_FANCY_PING */
- myid = getpid() & 0xFFFF;
- ping(*argv);
- return EXIT_SUCCESS;
+
+#if ENABLE_PING6
+int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ping6_main(int argc UNUSED_PARAM, char **argv)
+{
+ argv[0] = (char*)"-6";
+ return ping_main(0 /* argc+1 - but it's unused anyway */,
+ argv - 1);
}
-#endif /* ! CONFIG_FEATURE_FANCY_PING */
+#endif
-/*
+/* from ping6.c:
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
@@ -451,8 +791,8 @@ extern int ping_main(int argc, char **argv)
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
*
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
diff --git a/release/src/router/busybox/networking/ping6.c b/release/src/router/busybox/networking/ping6.c
deleted file mode 100644
index b68794c3..00000000
--- a/release/src/router/busybox/networking/ping6.c
+++ /dev/null
@@ -1,515 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * $Id: ping6.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
- * Mini ping implementation for busybox
- *
- * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * This version of ping is adapted from the ping in netkit-base 0.10,
- * which is:
- *
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Mike Muuss.
- *
- * Original copyright notice is retained at the end of this file.
- *
- * This version is an adaptation of ping.c from busybox.
- * The code was modified by Bart Visscher <magick@linux-fan.com>
- */
-
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/file.h>
-#include <sys/time.h>
-#include <sys/times.h>
-#include <sys/signal.h>
-
-#include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stddef.h> /* offsetof */
-#include "busybox.h"
-
-static const int DEFDATALEN = 56;
-static const int MAXIPLEN = 60;
-static const int MAXICMPLEN = 76;
-static const int MAXPACKET = 65468;
-#define MAX_DUP_CHK (8 * 128)
-static const int MAXWAIT = 10;
-static const int PINGINTERVAL = 1; /* second */
-
-#define O_QUIET (1 << 0)
-#define O_VERBOSE (1 << 1)
-
-#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
-#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
-#define SET(bit) (A(bit) |= B(bit))
-#define CLR(bit) (A(bit) &= (~B(bit)))
-#define TST(bit) (A(bit) & B(bit))
-
-static void ping(const char *host);
-
-/* simple version */
-#ifndef CONFIG_FEATURE_FANCY_PING6
-static struct hostent *h;
-
-void noresp(int ign)
-{
- printf("No response from %s\n", h->h_name);
- exit(EXIT_FAILURE);
-}
-
-static void ping(const char *host)
-{
- struct sockaddr_in6 pingaddr;
- struct icmp6_hdr *pkt;
- int pingsock, c;
- int sockopt;
- char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
-
- pingsock = create_icmp6_socket();
-
- memset(&pingaddr, 0, sizeof(struct sockaddr_in));
-
- pingaddr.sin6_family = AF_INET6;
- h = xgethostbyname2(host, AF_INET6);
- memcpy(&pingaddr.sin6_addr, h->h_addr, sizeof(pingaddr.sin6_addr));
-
- pkt = (struct icmp6_hdr *) packet;
- memset(pkt, 0, sizeof(packet));
- pkt->icmp6_type = ICMP6_ECHO_REQUEST;
-
- sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
- setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
- sizeof(sockopt));
-
- c = sendto(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
-
- if (c < 0 || c != sizeof(packet))
- bb_perror_msg_and_die("sendto");
-
- signal(SIGALRM, noresp);
- alarm(5); /* give the host 5000ms to respond */
- /* listen for replies */
- while (1) {
- struct sockaddr_in6 from;
- size_t fromlen = sizeof(from);
-
- if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &from, &fromlen)) < 0) {
- if (errno == EINTR)
- continue;
- bb_perror_msg("recvfrom");
- continue;
- }
- if (c >= 8) { /* icmp6_hdr */
- pkt = (struct icmp6_hdr *) packet;
- if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
- break;
- }
- }
- printf("%s is alive!\n", h->h_name);
- return;
-}
-
-extern int ping6_main(int argc, char **argv)
-{
- argc--;
- argv++;
- if (argc < 1)
- bb_show_usage();
- ping(*argv);
- return EXIT_SUCCESS;
-}
-
-#else /* ! CONFIG_FEATURE_FANCY_PING6 */
-/* full(er) version */
-static struct sockaddr_in6 pingaddr;
-static int pingsock = -1;
-static int datalen; /* intentionally uninitialized to work around gcc bug */
-static char* ifname;
-
-static long ntransmitted, nreceived, nrepeats, pingcount;
-static int myid, options;
-static unsigned long tmin = ULONG_MAX, tmax, tsum;
-static char rcvd_tbl[MAX_DUP_CHK / 8];
-
-# ifdef CONFIG_FEATURE_FANCY_PING
-extern
-# endif
- struct hostent *hostent;
-
-static void sendping(int);
-static void pingstats(int);
-static void unpack(char *, int, struct sockaddr_in6 *, int);
-
-/**************************************************************************/
-
-static void pingstats(int junk)
-{
- int status;
-
- signal(SIGINT, SIG_IGN);
-
- printf("\n--- %s ping statistics ---\n", hostent->h_name);
- printf("%ld packets transmitted, ", ntransmitted);
- printf("%ld packets received, ", nreceived);
- if (nrepeats)
- printf("%ld duplicates, ", nrepeats);
- if (ntransmitted)
- printf("%ld%% packet loss\n",
- (ntransmitted - nreceived) * 100 / ntransmitted);
- if (nreceived)
- printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
- tmin / 10, tmin % 10,
- (tsum / (nreceived + nrepeats)) / 10,
- (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
- if (nreceived != 0)
- status = EXIT_SUCCESS;
- else
- status = EXIT_FAILURE;
- exit(status);
-}
-
-static void sendping(int junk)
-{
- struct icmp6_hdr *pkt;
- int i;
- char packet[datalen + 8];
-
- pkt = (struct icmp6_hdr *) packet;
-
- pkt->icmp6_type = ICMP6_ECHO_REQUEST;
- pkt->icmp6_code = 0;
- pkt->icmp6_cksum = 0;
- pkt->icmp6_seq = ntransmitted++;
- pkt->icmp6_id = myid;
- CLR(pkt->icmp6_seq % MAX_DUP_CHK);
-
- gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
-
- i = sendto(pingsock, packet, sizeof(packet), 0,
- (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
-
- if (i < 0)
- bb_perror_msg_and_die("sendto");
- else if ((size_t)i != sizeof(packet))
- bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
- (int)sizeof(packet));
-
- signal(SIGALRM, sendping);
- if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
- alarm(PINGINTERVAL);
- } else { /* done, wait for the last ping to come back */
- /* todo, don't necessarily need to wait so long... */
- signal(SIGALRM, pingstats);
- alarm(MAXWAIT);
- }
-}
-
-static char *icmp6_type_name (int id)
-{
- switch (id) {
- case ICMP6_DST_UNREACH: return "Destination Unreachable";
- case ICMP6_PACKET_TOO_BIG: return "Packet too big";
- case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
- case ICMP6_PARAM_PROB: return "Parameter Problem";
- case ICMP6_ECHO_REPLY: return "Echo Reply";
- case ICMP6_ECHO_REQUEST: return "Echo Request";
- case ICMP6_MEMBERSHIP_QUERY: return "Membership Query";
- case ICMP6_MEMBERSHIP_REPORT: return "Membership Report";
- case ICMP6_MEMBERSHIP_REDUCTION: return "Membership Reduction";
- default: return "unknown ICMP type";
- }
-}
-
-static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
-{
- struct icmp6_hdr *icmppkt;
- struct timeval tv, *tp;
- int dupflag;
- unsigned long triptime;
- char buf[INET6_ADDRSTRLEN];
-
- gettimeofday(&tv, NULL);
-
- /* discard if too short */
- if (sz < (datalen + sizeof(struct icmp6_hdr)))
- return;
-
- icmppkt = (struct icmp6_hdr *) packet;
-
- if (icmppkt->icmp6_id != myid)
- return; /* not our ping */
-
- if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
- ++nreceived;
- tp = (struct timeval *) &icmppkt->icmp6_data8[4];
-
- if ((tv.tv_usec -= tp->tv_usec) < 0) {
- --tv.tv_sec;
- tv.tv_usec += 1000000;
- }
- tv.tv_sec -= tp->tv_sec;
-
- triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
- tsum += triptime;
- if (triptime < tmin)
- tmin = triptime;
- if (triptime > tmax)
- tmax = triptime;
-
- if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) {
- ++nrepeats;
- --nreceived;
- dupflag = 1;
- } else {
- SET(icmppkt->icmp6_seq % MAX_DUP_CHK);
- dupflag = 0;
- }
-
- if (options & O_QUIET)
- return;
-
- printf("%d bytes from %s: icmp6_seq=%u", sz,
- inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
- buf, sizeof(buf)),
- icmppkt->icmp6_seq);
- printf(" ttl=%d time=%lu.%lu ms", hoplimit,
- triptime / 10, triptime % 10);
- if (dupflag)
- printf(" (DUP!)");
- printf("\n");
- } else
- if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
- bb_error_msg("Warning: Got ICMP %d (%s)",
- icmppkt->icmp6_type, icmp6_type_name (icmppkt->icmp6_type));
-}
-
-static void ping(const char *host)
-{
- char packet[datalen + MAXIPLEN + MAXICMPLEN];
- char buf[INET6_ADDRSTRLEN];
- int sockopt;
- struct msghdr msg;
- struct sockaddr_in6 from;
- struct iovec iov;
- char control_buf[CMSG_SPACE(36)];
-
- pingsock = create_icmp6_socket();
-
- memset(&pingaddr, 0, sizeof(struct sockaddr_in));
-
- pingaddr.sin6_family = AF_INET6;
- hostent = xgethostbyname2(host, AF_INET6);
- if (hostent->h_addrtype != AF_INET6)
- bb_error_msg_and_die("unknown address type; only AF_INET6 is currently supported.");
-
- memcpy(&pingaddr.sin6_addr, hostent->h_addr, sizeof(pingaddr.sin6_addr));
-
-#ifdef ICMP6_FILTER
- {
- struct icmp6_filter filt;
- if (!(options & O_VERBOSE)) {
- ICMP6_FILTER_SETBLOCKALL(&filt);
-#if 0
- if ((options & F_FQDN) || (options & F_FQDNOLD) ||
- (options & F_NODEADDR) || (options & F_SUPTYPES))
- ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
- else
-#endif
- ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
- } else {
- ICMP6_FILTER_SETPASSALL(&filt);
- }
- if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
- sizeof(filt)) < 0)
- bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
- }
-#endif /*ICMP6_FILTER*/
-
- /* enable broadcast pings */
- sockopt = 1;
- setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
- sizeof(sockopt));
-
- /* set recv buf for broadcast pings */
- sockopt = 48 * 1024;
- setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
- sizeof(sockopt));
-
- sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
- setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
- sizeof(sockopt));
-
- sockopt = 1;
- setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, (char *) &sockopt,
- sizeof(sockopt));
-
- if (ifname) {
- if ((pingaddr.sin6_scope_id = if_nametoindex(ifname)) == 0)
- bb_error_msg_and_die("%s: invalid interface name", ifname);
- }
-
- printf("PING %s (%s): %d data bytes\n",
- hostent->h_name,
- inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
- buf, sizeof(buf)),
- datalen);
-
- signal(SIGINT, pingstats);
-
- /* start the ping's going ... */
- sendping(0);
-
- /* listen for replies */
- msg.msg_name=&from;
- msg.msg_namelen=sizeof(from);
- msg.msg_iov=&iov;
- msg.msg_iovlen=1;
- msg.msg_control=control_buf;
- iov.iov_base=packet;
- iov.iov_len=sizeof(packet);
- while (1) {
- int c;
- struct cmsghdr *cmsgptr = NULL;
- int hoplimit=-1;
- msg.msg_controllen=sizeof(control_buf);
-
- if ((c = recvmsg(pingsock, &msg, 0)) < 0) {
- if (errno == EINTR)
- continue;
- bb_perror_msg("recvfrom");
- continue;
- }
- for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
- cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
- if (cmsgptr->cmsg_level == SOL_IPV6 &&
- cmsgptr->cmsg_type == IPV6_HOPLIMIT ) {
- hoplimit=*(int*)CMSG_DATA(cmsgptr);
- }
- }
- unpack(packet, c, &from, hoplimit);
- if (pingcount > 0 && nreceived >= pingcount)
- break;
- }
- pingstats(0);
-}
-
-extern int ping6_main(int argc, char **argv)
-{
- char *thisarg;
-
- datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
-
- argc--;
- argv++;
- options = 0;
- /* Parse any options */
- while (argc >= 1 && **argv == '-') {
- thisarg = *argv;
- thisarg++;
- switch (*thisarg) {
- case 'v':
- options &= ~O_QUIET;
- options |= O_VERBOSE;
- break;
- case 'q':
- options &= ~O_VERBOSE;
- options |= O_QUIET;
- break;
- case 'c':
- if (--argc <= 0)
- bb_show_usage();
- argv++;
- pingcount = atoi(*argv);
- break;
- case 's':
- if (--argc <= 0)
- bb_show_usage();
- argv++;
- datalen = atoi(*argv);
- break;
- case 'I':
- if (--argc <= 0)
- bb_show_usage();
- argv++;
- ifname = *argv;
- break;
- default:
- bb_show_usage();
- }
- argc--;
- argv++;
- }
- if (argc < 1)
- bb_show_usage();
-
- myid = getpid() & 0xFFFF;
- ping(*argv);
- return EXIT_SUCCESS;
-}
-#endif /* ! CONFIG_FEATURE_FANCY_PING6 */
-
-/*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Mike Muuss.
- *
- * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
- *
- * 4. 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.
- */
diff --git a/release/src/router/busybox/networking/pscan.c b/release/src/router/busybox/networking/pscan.c
new file mode 100644
index 00000000..5fb6af0b
--- /dev/null
+++ b/release/src/router/busybox/networking/pscan.c
@@ -0,0 +1,154 @@
+/*
+ * Pscan is a mini port scanner implementation for busybox
+ *
+ * Copyright 2007 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+
+/* debugging */
+#ifdef DEBUG_PSCAN
+#define DMSG(...) bb_error_msg(__VA_ARGS__)
+#define DERR(...) bb_perror_msg(__VA_ARGS__)
+#else
+#define DMSG(...) ((void)0)
+#define DERR(...) ((void)0)
+#endif
+
+static const char *port_name(unsigned port)
+{
+ struct servent *server;
+
+ server = getservbyport(htons(port), NULL);
+ if (server)
+ return server->s_name;
+ return "unknown";
+}
+
+/* We don't expect to see 1000+ seconds delay, unsigned is enough */
+#define MONOTONIC_US() ((unsigned)monotonic_us())
+
+int pscan_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pscan_main(int argc UNUSED_PARAM, char **argv)
+{
+ const char *opt_max_port = "1024"; /* -P: default max port */
+ const char *opt_min_port = "1"; /* -p: default min port */
+ const char *opt_timeout = "5000"; /* -t: default timeout in msec */
+ /* We estimate rtt and wait rtt*4 before concluding that port is
+ * totally blocked. min rtt of 5 ms may be too low if you are
+ * scanning an Internet host behind saturated/traffic shaped link.
+ * Rule of thumb: with min_rtt of N msec, scanning 1000 ports
+ * will take N seconds at absolute minimum */
+ const char *opt_min_rtt = "5"; /* -T: default min rtt in msec */
+ const char *result_str;
+ len_and_sockaddr *lsap;
+ int s;
+ unsigned opt;
+ unsigned port, max_port, nports;
+ unsigned closed_ports = 0;
+ unsigned open_ports = 0;
+ /* all in usec */
+ unsigned timeout;
+ unsigned min_rtt;
+ unsigned rtt_4;
+ unsigned start, diff;
+
+ opt_complementary = "=1"; /* exactly one non-option */
+ opt = getopt32(argv, "cbp:P:t:T:", &opt_min_port, &opt_max_port, &opt_timeout, &opt_min_rtt);
+ argv += optind;
+ max_port = xatou_range(opt_max_port, 1, 65535);
+ port = xatou_range(opt_min_port, 1, max_port);
+ nports = max_port - port + 1;
+ min_rtt = xatou_range(opt_min_rtt, 1, INT_MAX/1000 / 4) * 1000;
+ timeout = xatou_range(opt_timeout, 1, INT_MAX/1000 / 4) * 1000;
+ /* Initial rtt is BIG: */
+ rtt_4 = timeout;
+
+ DMSG("min_rtt %u timeout %u", min_rtt, timeout);
+
+ lsap = xhost2sockaddr(*argv, port);
+ printf("Scanning %s ports %u to %u\n Port\tProto\tState\tService\n",
+ *argv, port, max_port);
+
+ for (; port <= max_port; port++) {
+ DMSG("rtt %u", rtt_4);
+
+ /* The SOCK_STREAM socket type is implemented on the TCP/IP protocol. */
+ set_nport(lsap, htons(port));
+ s = xsocket(lsap->u.sa.sa_family, SOCK_STREAM, 0);
+ /* We need unblocking socket so we don't need to wait for ETIMEOUT. */
+ /* Nonblocking connect typically "fails" with errno == EINPROGRESS */
+ ndelay_on(s);
+
+ DMSG("connect to port %u", port);
+ result_str = NULL;
+ start = MONOTONIC_US();
+ if (connect(s, &lsap->u.sa, lsap->len) == 0) {
+ /* Unlikely, for me even localhost fails :) */
+ DMSG("connect succeeded");
+ goto open;
+ }
+ /* Check for untypical errors... */
+ if (errno != EAGAIN && errno != EINPROGRESS
+ && errno != ECONNREFUSED
+ ) {
+ bb_perror_nomsg_and_die();
+ }
+
+ diff = 0;
+ while (1) {
+ if (errno == ECONNREFUSED) {
+ if (opt & 1) /* -c: show closed too */
+ result_str = "closed";
+ closed_ports++;
+ break;
+ }
+ DERR("port %u errno %d @%u", port, errno, diff);
+
+ if (diff > rtt_4) {
+ if (opt & 2) /* -b: show blocked too */
+ result_str = "blocked";
+ break;
+ }
+ /* Can sleep (much) longer than specified delay.
+ * We check rtt BEFORE we usleep, otherwise
+ * on localhost we'll have no writes done (!)
+ * before we exceed (rather small) rtt */
+ usleep(rtt_4/8);
+ open:
+ diff = MONOTONIC_US() - start;
+ DMSG("write to port %u @%u", port, diff - start);
+ if (write(s, " ", 1) >= 0) { /* We were able to write to the socket */
+ open_ports++;
+ result_str = "open";
+ break;
+ }
+ }
+ DMSG("out of loop @%u", diff);
+ if (result_str)
+ printf("%5u" "\t" "tcp" "\t" "%s" "\t" "%s" "\n",
+ port, result_str, port_name(port));
+
+ /* Estimate new rtt - we don't want to wait entire timeout
+ * for each port. *4 allows for rise in net delay.
+ * We increase rtt quickly (rtt_4*4), decrease slowly
+ * (diff is at least rtt_4/8, *4 == rtt_4/2)
+ * because we don't want to accidentally miss ports. */
+ rtt_4 = diff * 4;
+ if (rtt_4 < min_rtt)
+ rtt_4 = min_rtt;
+ if (rtt_4 > timeout)
+ rtt_4 = timeout;
+ /* Clean up */
+ close(s);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) free(lsap);
+
+ printf("%d closed, %d open, %d timed out (or blocked) ports\n",
+ closed_ports,
+ open_ports,
+ nports - (closed_ports + open_ports));
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/networking/route.c b/release/src/router/busybox/networking/route.c
index ff74cfd1..5d254080 100644
--- a/release/src/router/busybox/networking/route.c
+++ b/release/src/router/busybox/networking/route.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/* route
*
* Similar to the standard Unix route, but with only the necessary
@@ -9,13 +10,8 @@
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
* (derived from FvK's 'route.c 1.70 01/04/94')
*
- * This program is free software; you can redistribute it
- * and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at
- * your option) any later version.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
- * $Id: route.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
*
* displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
* adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
@@ -23,46 +19,38 @@
* IPV6 support added by Bart Visscher <magick@linux-fan.com>
*/
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include "inet_common.h"
+/* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Rewritten to fix several bugs, add additional error checking, and
+ * remove ridiculous amounts of bloat.
+ */
+
#include <net/route.h>
#include <net/if.h>
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <ctype.h>
-#include "busybox.h"
-
-#define _(x) x
-
-#define RTACTION_ADD 1
-#define RTACTION_DEL 2
-#define RTACTION_HELP 3
-#define RTACTION_FLUSH 4
-#define RTACTION_SHOW 5
-
-#define E_NOTFOUND 8
-#define E_SOCK 7
-#define E_LOOKUP 6
-#define E_VERSION 5
-#define E_USAGE 4
-#define E_OPTERR 3
-#define E_INTERN 2
-#define E_NOSUPP 1
-
-#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
-#define HAVE_NEW_ADDRT 1
+
+#include "libbb.h"
+#include "inet_common.h"
+
+
+#ifndef RTF_UP
+/* Keep this in sync with /usr/src/linux/include/linux/route.h */
+#define RTF_UP 0x0001 /* route usable */
+#define RTF_GATEWAY 0x0002 /* destination is a gateway */
+#define RTF_HOST 0x0004 /* host entry (net otherwise) */
+#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
+#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
+#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
+#define RTF_MTU 0x0040 /* specific MTU for this route */
+#ifndef RTF_MSS
+#define RTF_MSS RTF_MTU /* Compatibility :-( */
#endif
-#ifdef RTF_IRTT /* route */
-#define HAVE_RTF_IRTT 1
+#define RTF_WINDOW 0x0080 /* per route window clamping */
+#define RTF_IRTT 0x0100 /* Initial round trip time */
+#define RTF_REJECT 0x0200 /* Reject route */
#endif
-#ifdef RTF_REJECT /* route */
-#define HAVE_RTF_REJECT 1
+
+#if defined(SIOCADDRTOLD) || defined(RTF_IRTT) /* route */
+#define HAVE_NEW_ADDRT 1
#endif
#if HAVE_NEW_ADDRT
@@ -73,633 +61,639 @@
#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
#endif
+/* The RTACTION entries must agree with tbl_verb[] below! */
+#define RTACTION_ADD 1
+#define RTACTION_DEL 2
+
+/* For the various tbl_*[] arrays, the 1st byte is the offset to
+ * the next entry and the 2nd byte is return value. */
+
+#define NET_FLAG 1
+#define HOST_FLAG 2
+
+/* We remap '-' to '#' to avoid problems with getopt. */
+static const char tbl_hash_net_host[] ALIGN1 =
+ "\007\001#net\0"
+/* "\010\002#host\0" */
+ "\007\002#host" /* Since last, we can save a byte. */
+;
+
+#define KW_TAKES_ARG 020
+#define KW_SETS_FLAG 040
+
+#define KW_IPVx_METRIC 020
+#define KW_IPVx_NETMASK 021
+#define KW_IPVx_GATEWAY 022
+#define KW_IPVx_MSS 023
+#define KW_IPVx_WINDOW 024
+#define KW_IPVx_IRTT 025
+#define KW_IPVx_DEVICE 026
+
+#define KW_IPVx_FLAG_ONLY 040
+#define KW_IPVx_REJECT 040
+#define KW_IPVx_MOD 041
+#define KW_IPVx_DYN 042
+#define KW_IPVx_REINSTATE 043
+
+static const char tbl_ipvx[] ALIGN1 =
+ /* 020 is the "takes an arg" bit */
+#if HAVE_NEW_ADDRT
+ "\011\020metric\0"
+#endif
+ "\012\021netmask\0"
+ "\005\022gw\0"
+ "\012\022gateway\0"
+ "\006\023mss\0"
+ "\011\024window\0"
+#ifdef RTF_IRTT
+ "\007\025irtt\0"
+#endif
+ "\006\026dev\0"
+ "\011\026device\0"
+ /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
+#ifdef RTF_REJECT
+ "\011\040reject\0"
+#endif
+ "\006\041mod\0"
+ "\006\042dyn\0"
+/* "\014\043reinstate\0" */
+ "\013\043reinstate" /* Since last, we can save a byte. */
+;
+
+static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+#ifdef RTF_REJECT
+ RTF_REJECT,
+#endif
+ RTF_MODIFIED,
+ RTF_DYNAMIC,
+ RTF_REINSTATE
+};
+static int kw_lookup(const char *kwtbl, char ***pargs)
+{
+ if (**pargs) {
+ do {
+ if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
+ *pargs += 1;
+ if (kwtbl[1] & KW_TAKES_ARG) {
+ if (!**pargs) { /* No more args! */
+ bb_show_usage();
+ }
+ *pargs += 1; /* Calling routine will use args[-1]. */
+ }
+ return kwtbl[1];
+ }
+ kwtbl += *kwtbl;
+ } while (*kwtbl);
+ }
+ return 0;
+}
-/* add or delete a route depending on action */
+/* Add or delete a route, depending on action. */
-static int INET_setroute(int action, int options, char **args)
+static void INET_setroute(int action, char **args)
{
struct rtentry rt;
- char target[128], gateway[128] = "NONE";
- const char *netmask = bb_INET_default;
- int xflag, isnet;
- int skfd;
+ const char *netmask = NULL;
+ int skfd, isnet, xflag;
- xflag = 0;
+ /* Grab the -net or -host options. Remember they were transformed. */
+ xflag = kw_lookup(tbl_hash_net_host, &args);
- if (*args == NULL)
+ /* If we did grab -net or -host, make sure we still have an arg left. */
+ if (*args == NULL) {
bb_show_usage();
- if (strcmp(*args, "-net") == 0) {
- xflag = 1;
- args++;
- } else if (strcmp(*args, "-host") == 0) {
- xflag = 2;
- args++;
}
- if (*args == NULL)
- bb_show_usage();
- safe_strncpy(target, *args++, (sizeof target));
/* Clean out the RTREQ structure. */
- memset((char *) &rt, 0, sizeof(struct rtentry));
+ memset(&rt, 0, sizeof(rt));
+ {
+ const char *target = *args++;
+ char *prefix;
- if ((isnet =
- INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
- xflag != 1)) < 0) {
- bb_error_msg(_("can't resolve %s"), target);
- return EXIT_FAILURE; /* XXX change to E_something */
- }
-
- switch (xflag) {
- case 1:
- isnet = 1;
- break;
+ /* recognize x.x.x.x/mask format. */
+ prefix = strchr(target, '/');
+ if (prefix) {
+ int prefix_len;
- case 2:
- isnet = 0;
- break;
+ prefix_len = xatoul_range(prefix+1, 0, 32);
+ mask_in_addr(rt) = htonl( ~ (0xffffffffUL >> prefix_len));
+ *prefix = '\0';
+#if HAVE_NEW_ADDRT
+ rt.rt_genmask.sa_family = AF_INET;
+#endif
+ } else {
+ /* Default netmask. */
+ netmask = bb_str_default;
+ }
+ /* Prefer hostname lookup is -host flag (xflag==1) was given. */
+ isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+ (xflag & HOST_FLAG));
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
+ if (prefix) {
+ /* do not destroy prefix for process args */
+ *prefix = '/';
+ }
+ }
- default:
- break;
+ if (xflag) { /* Reinit isnet if -net or -host was specified. */
+ isnet = (xflag & NET_FLAG);
}
/* Fill in the other fields. */
- rt.rt_flags = (RTF_UP | RTF_HOST);
- if (isnet)
- rt.rt_flags &= ~RTF_HOST;
+ rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
while (*args) {
- if (strcmp(*args, "metric") == 0) {
- int metric;
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
+
+ if (k & KW_IPVx_FLAG_ONLY) {
+ rt.rt_flags |= flags_ipvx[k & 3];
+ continue;
+ }
- args++;
- if (!*args || !isdigit(**args))
- bb_show_usage();
- metric = atoi(*args);
#if HAVE_NEW_ADDRT
- rt.rt_metric = metric + 1;
-#else
- ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */
-#endif
- args++;
+ if (k == KW_IPVx_METRIC) {
+ rt.rt_metric = xatoul(args_m1) + 1;
continue;
}
+#endif
- if (strcmp(*args, "netmask") == 0) {
+ if (k == KW_IPVx_NETMASK) {
struct sockaddr mask;
- args++;
- if (!*args || mask_in_addr(rt))
+ if (mask_in_addr(rt)) {
bb_show_usage();
- netmask = *args;
- if ((isnet =
- INET_resolve(netmask, (struct sockaddr_in *) &mask,
- 0)) < 0) {
- bb_error_msg(_("can't resolve netmask %s"), netmask);
- return E_LOOKUP;
+ }
+
+ netmask = args_m1;
+ isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", netmask);
}
rt.rt_genmask = full_mask(mask);
- args++;
continue;
}
- if (strcmp(*args, "gw") == 0 || strcmp(*args, "gateway") == 0) {
- args++;
- if (!*args)
- bb_show_usage();
- if (rt.rt_flags & RTF_GATEWAY)
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rt_flags & RTF_GATEWAY) {
bb_show_usage();
- safe_strncpy(gateway, *args, (sizeof gateway));
- if ((isnet =
- INET_resolve(gateway, (struct sockaddr_in *) &rt.rt_gateway,
- 1)) < 0) {
- bb_error_msg(_("can't resolve gw %s"), gateway);
- return E_LOOKUP;
}
+
+ isnet = INET_resolve(args_m1,
+ (struct sockaddr_in *) &rt.rt_gateway, 1);
+ rt.rt_flags |= RTF_GATEWAY;
+
if (isnet) {
- bb_error_msg(_("%s: cannot use a NETWORK as gateway!"), gateway);
- return E_OPTERR;
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
+ }
+ bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
}
- rt.rt_flags |= RTF_GATEWAY;
- args++;
continue;
}
- if (strcmp(*args, "mss") == 0) {
- args++;
+ if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
rt.rt_flags |= RTF_MSS;
- if (!*args)
- bb_show_usage();
- rt.rt_mss = atoi(*args);
- args++;
- if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
- bb_error_msg(_("Invalid MSS."));
- return E_OPTERR;
- }
+ rt.rt_mss = xatoul_range(args_m1, 64, 32768);
continue;
}
- if (strcmp(*args, "window") == 0) {
- args++;
- if (!*args)
- bb_show_usage();
+ if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */
rt.rt_flags |= RTF_WINDOW;
- rt.rt_window = atoi(*args);
- args++;
- if (rt.rt_window < 128) {
- bb_error_msg(_("Invalid window."));
- return E_OPTERR;
- }
+ rt.rt_window = xatoul_range(args_m1, 128, INT_MAX);
continue;
}
- if (strcmp(*args, "irtt") == 0) {
- args++;
- if (!*args)
- bb_show_usage();
- args++;
-#if HAVE_RTF_IRTT
+#ifdef RTF_IRTT
+ if (k == KW_IPVx_IRTT) {
rt.rt_flags |= RTF_IRTT;
- rt.rt_irtt = atoi(*(args - 1));
+ rt.rt_irtt = xatoul(args_m1);
rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */
#if 0 /* FIXME: do we need to check anything of this? */
if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
- bb_error_msg(_("Invalid initial rtt."));
- return E_OPTERR;
+ bb_error_msg_and_die("bad irtt");
}
#endif
-#else
- ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
-#endif
continue;
}
-
- if (strcmp(*args, "reject") == 0) {
- args++;
-#if HAVE_RTF_REJECT
- rt.rt_flags |= RTF_REJECT;
-#else
- ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
#endif
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't requre the dev/device keyword in that case. */
+ if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ rt.rt_dev = args[-1];
continue;
}
- if (strcmp(*args, "mod") == 0) {
- args++;
- rt.rt_flags |= RTF_MODIFIED;
- continue;
- }
- if (strcmp(*args, "dyn") == 0) {
- args++;
- rt.rt_flags |= RTF_DYNAMIC;
- continue;
- }
- if (strcmp(*args, "reinstate") == 0) {
- args++;
- rt.rt_flags |= RTF_REINSTATE;
- continue;
- }
- if (strcmp(*args, "device") == 0 || strcmp(*args, "dev") == 0) {
- args++;
- if (rt.rt_dev || *args == NULL)
- bb_show_usage();
- rt.rt_dev = *args++;
- continue;
- }
- /* nothing matches */
- if (!rt.rt_dev) {
- rt.rt_dev = *args++;
- if (*args)
- bb_show_usage(); /* must be last to catch typos */
- } else {
- bb_show_usage();
- }
+
+ /* Nothing matched. */
+ bb_show_usage();
}
-#if HAVE_RTF_REJECT
- if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
- rt.rt_dev = "lo";
+#ifdef RTF_REJECT
+ if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
+ rt.rt_dev = (char*)"lo";
+ }
#endif
/* sanity checks.. */
if (mask_in_addr(rt)) {
- unsigned long mask = mask_in_addr(rt);
+ uint32_t mask = mask_in_addr(rt);
mask = ~ntohl(mask);
if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
- bb_error_msg(_("netmask %.8x doesn't make sense with host route"),
- (unsigned int) mask);
- return E_OPTERR;
+ bb_error_msg_and_die("netmask %.8x and host route conflict",
+ (unsigned int) mask);
}
if (mask & (mask + 1)) {
- bb_error_msg(_("bogus netmask %s"), netmask);
- return E_OPTERR;
+ bb_error_msg_and_die("bogus netmask %s", netmask);
}
mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
- if (mask & ~mask_in_addr(rt)) {
- bb_error_msg(_("netmask doesn't match route address"));
- return E_OPTERR;
+ if (mask & ~(uint32_t)mask_in_addr(rt)) {
+ bb_error_msg_and_die("netmask and route address conflict");
}
}
+
/* Fill out netmask if still unset */
- if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
+ if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
mask_in_addr(rt) = 0xffffffff;
+ }
/* Create a socket to the INET kernel. */
- if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- perror("socket");
- return E_SOCK;
- }
- /* Tell the kernel to accept this route. */
- if (action == RTACTION_DEL) {
- if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
- perror("SIOCDELRT");
- close(skfd);
- return E_SOCK;
- }
- } else {
- if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
- perror("SIOCADDRT");
- close(skfd);
- return E_SOCK;
- }
- }
+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
- /* Close the socket. */
- (void) close(skfd);
- return EXIT_SUCCESS;
+ if (action == RTACTION_ADD)
+ xioctl(skfd, SIOCADDRT, &rt);
+ else
+ xioctl(skfd, SIOCDELRT, &rt);
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
}
-#ifdef CONFIG_FEATURE_IPV6
-static int INET6_setroute(int action, int options, char **args)
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_setroute(int action, char **args)
{
- struct in6_rtmsg rt;
- struct ifreq ifr;
struct sockaddr_in6 sa6;
- char target[128], gateway[128] = "NONE";
- int metric, prefix_len;
- char *devname = NULL;
- char *cp;
- int skfd;
+ struct in6_rtmsg rt;
+ int prefix_len, skfd;
+ const char *devname;
- if (*args == NULL)
- bb_show_usage();
+ /* We know args isn't NULL from the check in route_main. */
+ const char *target = *args++;
- strcpy(target, *args++);
- if (!strcmp(target, "default")) {
- prefix_len = 0;
- memset(&sa6, 0, sizeof(sa6));
- } else {
- if ((cp = strchr(target, '/'))) {
- prefix_len = atol(cp + 1);
- if ((prefix_len < 0) || (prefix_len > 128))
- bb_show_usage();
- *cp = 0;
+ if (strcmp(target, bb_str_default) == 0) {
+ prefix_len = 0;
+ memset(&sa6, 0, sizeof(sa6));
} else {
- prefix_len = 128;
- }
- if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
- bb_error_msg(_("can't resolve %s"), target);
- return EXIT_FAILURE; /* XXX change to E_something */
+ char *cp;
+ cp = strchr(target, '/'); /* Yes... const to non is ok. */
+ if (cp) {
+ *cp = '\0';
+ prefix_len = xatoul_range(cp + 1, 0, 128);
+ } else {
+ prefix_len = 128;
+ }
+ if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
}
- }
/* Clean out the RTREQ structure. */
- memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
+ memset(&rt, 0, sizeof(rt));
memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
/* Fill in the other fields. */
- rt.rtmsg_flags = RTF_UP;
- if (prefix_len == 128)
- rt.rtmsg_flags |= RTF_HOST;
- rt.rtmsg_metric = 1;
rt.rtmsg_dst_len = prefix_len;
+ rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
+ rt.rtmsg_metric = 1;
+
+ devname = NULL;
while (*args) {
- if (!strcmp(*args, "metric")) {
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
- args++;
- if (!*args || !isdigit(**args))
- bb_show_usage();
- metric = atoi(*args);
- rt.rtmsg_metric = metric;
- args++;
+ if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
+ rt.rtmsg_flags |= flags_ipvx[k & 3];
continue;
}
- if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
- args++;
- if (!*args)
- bb_show_usage();
- if (rt.rtmsg_flags & RTF_GATEWAY)
+
+ if (k == KW_IPVx_METRIC) {
+ rt.rtmsg_metric = xatoul(args_m1);
+ continue;
+ }
+
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rtmsg_flags & RTF_GATEWAY) {
bb_show_usage();
- strcpy(gateway, *args);
- if (INET6_resolve(gateway, (struct sockaddr_in6 *) &sa6) < 0) {
- bb_error_msg(_("can't resolve gw %s"), gateway);
- return (E_LOOKUP);
+ }
+
+ if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
}
memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
sizeof(struct in6_addr));
rt.rtmsg_flags |= RTF_GATEWAY;
- args++;
continue;
}
- if (!strcmp(*args, "mod")) {
- args++;
- rt.rtmsg_flags |= RTF_MODIFIED;
- continue;
- }
- if (!strcmp(*args, "dyn")) {
- args++;
- rt.rtmsg_flags |= RTF_DYNAMIC;
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't requre the dev/device keyword in that case. */
+ if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ devname = args[-1];
continue;
}
- if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
- args++;
- if (!*args)
- bb_show_usage();
- } else if (args[1])
- bb_show_usage();
- devname = *args;
- args++;
+ /* Nothing matched. */
+ bb_show_usage();
}
/* Create a socket to the INET6 kernel. */
- if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- perror("socket");
- return (E_SOCK);
- }
+ skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
+
+ rt.rtmsg_ifindex = 0;
+
if (devname) {
+ struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, devname);
-
- if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
- perror("SIOGIFINDEX");
- return (E_SOCK);
- }
+ strncpy_IFNAMSIZ(ifr.ifr_name, devname);
+ xioctl(skfd, SIOGIFINDEX, &ifr);
rt.rtmsg_ifindex = ifr.ifr_ifindex;
- } else
- rt.rtmsg_ifindex = 0;
+ }
/* Tell the kernel to accept this route. */
- if (action == RTACTION_DEL) {
- if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
- perror("SIOCDELRT");
- close(skfd);
- return (E_SOCK);
- }
- } else {
- if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
- perror("SIOCADDRT");
- close(skfd);
- return (E_SOCK);
- }
- }
+ if (action == RTACTION_ADD)
+ xioctl(skfd, SIOCADDRT, &rt);
+ else
+ xioctl(skfd, SIOCDELRT, &rt);
- /* Close the socket. */
- (void) close(skfd);
- return (0);
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
}
#endif
-#ifndef RTF_UP
-/* Keep this in sync with /usr/src/linux/include/linux/route.h */
-#define RTF_UP 0x0001 /* route usable */
-#define RTF_GATEWAY 0x0002 /* destination is a gateway */
-#define RTF_HOST 0x0004 /* host entry (net otherwise) */
-#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
-#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
-#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
-#define RTF_MTU 0x0040 /* specific MTU for this route */
-#ifndef RTF_MSS
-#define RTF_MSS RTF_MTU /* Compatibility :-( */
+static const unsigned flagvals[] = { /* Must agree with flagchars[]. */
+ RTF_GATEWAY,
+ RTF_HOST,
+ RTF_REINSTATE,
+ RTF_DYNAMIC,
+ RTF_MODIFIED,
+#if ENABLE_FEATURE_IPV6
+ RTF_DEFAULT,
+ RTF_ADDRCONF,
+ RTF_CACHE
#endif
-#define RTF_WINDOW 0x0080 /* per route window clamping */
-#define RTF_IRTT 0x0100 /* Initial round trip time */
-#define RTF_REJECT 0x0200 /* Reject route */
+};
+
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
+
+/* Must agree with flagvals[]. */
+static const char flagchars[] ALIGN1 =
+ "GHRDM"
+#if ENABLE_FEATURE_IPV6
+ "DAC"
#endif
+;
-void displayroutes(int noresolve, int netstatfmt)
+static void set_flags(char *flagstr, int flags)
{
- char buff[256];
- int nl = 0;
- struct in_addr dest;
- struct in_addr gw;
- struct in_addr mask;
+ int i;
+
+ *flagstr++ = 'U';
+
+ for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
+ if (flags & flagvals[i]) {
+ ++flagstr;
+ }
+ }
+}
+
+/* also used in netstat */
+void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
+{
+ char devname[64], flags[16], *sdest, *sgw;
+ unsigned long d, g, m;
int flgs, ref, use, metric, mtu, win, ir;
- char flags[64];
- unsigned long int d, g, m;
-
- char sdest[16], sgw[16];
-
- FILE *fp = bb_xfopen("/proc/net/route", "r");
-
- if (noresolve)
- noresolve = 0x0fff;
-
- printf("Kernel IP routing table\n");
- printf
- ("Destination Gateway Genmask Flags %s Iface\n",
- netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
-
- while (fgets(buff, sizeof(buff), fp) != NULL) {
- if (nl) {
- int ifl = 0;
- int numeric;
- struct sockaddr_in s_addr;
-
- while (buff[ifl] != ' ' && buff[ifl] != '\t' && buff[ifl] != '\0')
- ifl++;
- buff[ifl] = 0; /* interface */
- if (sscanf(buff + ifl + 1, "%lx%lx%X%d%d%d%lx%d%d%d",
- &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win,
- &ir) != 10) {
- bb_error_msg_and_die("Unsuported kernel route format\n");
- }
- ifl = 0; /* parse flags */
- if (flgs & RTF_UP) {
- if (flgs & RTF_REJECT)
- flags[ifl++] = '!';
- else
- flags[ifl++] = 'U';
- if (flgs & RTF_GATEWAY)
- flags[ifl++] = 'G';
- if (flgs & RTF_HOST)
- flags[ifl++] = 'H';
- if (flgs & RTF_REINSTATE)
- flags[ifl++] = 'R';
- if (flgs & RTF_DYNAMIC)
- flags[ifl++] = 'D';
- if (flgs & RTF_MODIFIED)
- flags[ifl++] = 'M';
- flags[ifl] = 0;
- dest.s_addr = d;
- gw.s_addr = g;
- mask.s_addr = m;
- memset(&s_addr, 0, sizeof(struct sockaddr_in));
- s_addr.sin_family = AF_INET;
- s_addr.sin_addr = dest;
- numeric = noresolve | 0x8000; /* default instead of * */
- INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
- numeric = noresolve | 0x4000; /* host instead of net */
- s_addr.sin_addr = gw;
- INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
-
- printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask),
- flags);
- if (netstatfmt)
- printf("%5d %-5d %6d %s\n", mtu, win, ir, buff);
- else
- printf("%-6d %-2d %7d %s\n", metric, ref, use, buff);
+ struct sockaddr_in s_addr;
+ struct in_addr mask;
+
+ FILE *fp = xfopen_for_read("/proc/net/route");
+
+ printf("Kernel IP routing table\n"
+ "Destination Gateway Genmask Flags %s Iface\n",
+ netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
+
+ if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
+ goto ERROR; /* Empty or missing line, or read error. */
+ }
+ while (1) {
+ int r;
+ r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
+ devname, &d, &g, &flgs, &ref, &use, &metric, &m,
+ &mtu, &win, &ir);
+ if (r != 11) {
+ if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+ break;
}
+ ERROR:
+ bb_error_msg_and_die("fscanf");
+ }
+
+ if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
+ continue;
+ }
+
+ set_flags(flags, (flgs & IPV4_MASK));
+#ifdef RTF_REJECT
+ if (flgs & RTF_REJECT) {
+ flags[0] = '!';
+ }
+#endif
+
+ memset(&s_addr, 0, sizeof(struct sockaddr_in));
+ s_addr.sin_family = AF_INET;
+ s_addr.sin_addr.s_addr = d;
+ sdest = INET_rresolve(&s_addr, (noresolve | 0x8000), m); /* 'default' instead of '*' */
+ s_addr.sin_addr.s_addr = g;
+ sgw = INET_rresolve(&s_addr, (noresolve | 0x4000), m); /* Host instead of net */
+ mask.s_addr = m;
+ /* "%15.15s" truncates hostnames, do we really want that? */
+ printf("%-15.15s %-15.15s %-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+ free(sdest);
+ free(sgw);
+ if (netstatfmt) {
+ printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
+ } else {
+ printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
}
- nl++;
}
}
-#ifdef CONFIG_FEATURE_IPV6
-static void INET6_displayroutes(int noresolve)
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_displayroutes(void)
{
- char buff[256];
+ char addr6[128], *naddr6;
+ /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
+ * We read the non-delimited strings into the tail of the buffer
+ * using fscanf and then modify the buffer by shifting forward
+ * while inserting ':'s and the nul terminator for the first string.
+ * Hence the strings are at addr6x and addr6x+40. This generates
+ * _much_ less code than the previous (upstream) approach. */
+ char addr6x[80];
char iface[16], flags[16];
- char addr6[128], naddr6[128];
- struct sockaddr_in6 saddr6, snaddr6;
int iflags, metric, refcnt, use, prefix_len, slen;
- int numeric;
-
- char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];
-
- FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r");
-
- flags[0] = 'U';
-
- if (noresolve)
- noresolve = 0x0fff;
- numeric = noresolve | 0x8000; /* default instead of * */
-
- printf("Kernel IPv6 routing table\n"
- "Destination "
- "Next Hop "
- "Flags Metric Ref Use Iface\n");
-
- while (fgets(buff, sizeof(buff), fp) != NULL) {
- int ifl;
-
- if (sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
- "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
- "%4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
- addr6p[0], addr6p[1], addr6p[2], addr6p[3],
- addr6p[4], addr6p[5], addr6p[6], addr6p[7],
- &prefix_len,
- saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
- saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
- &slen,
- naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
- naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
- &metric, &use, &refcnt, &iflags, iface) != 31) {
- bb_error_msg_and_die("Unsuported kernel route format\n");
+ struct sockaddr_in6 snaddr6;
+
+ FILE *fp = xfopen_for_read("/proc/net/ipv6_route");
+
+ printf("Kernel IPv6 routing table\n%-44s%-40s"
+ "Flags Metric Ref Use Iface\n",
+ "Destination", "Next Hop");
+
+ while (1) {
+ int r;
+ r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
+ addr6x+14, &prefix_len, &slen, addr6x+40+7,
+ &metric, &use, &refcnt, &iflags, iface);
+ if (r != 9) {
+ if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+ break;
+ }
+ ERROR:
+ bb_error_msg_and_die("fscanf");
}
- ifl = 1; /* parse flags */
- if (!(iflags & RTF_UP))
+ /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
+ * For now, always do this to validate the proc route format, even
+ * if the interface is down. */
+ {
+ int i = 0;
+ char *p = addr6x+14;
+
+ do {
+ if (!*p) {
+ if (i == 40) { /* nul terminator for 1st address? */
+ addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
+ ++p; /* Skip and continue. */
+ continue;
+ }
+ goto ERROR;
+ }
+ addr6x[i++] = *p++;
+ if (!((i+1) % 5)) {
+ addr6x[i++] = ':';
+ }
+ } while (i < 40+28+7);
+ }
+
+ if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
continue;
- if (iflags & RTF_GATEWAY)
- flags[ifl++] = 'G';
- if (iflags & RTF_HOST)
- flags[ifl++] = 'H';
- if (iflags & RTF_DEFAULT)
- flags[ifl++] = 'D';
- if (iflags & RTF_ADDRCONF)
- flags[ifl++] = 'A';
- if (iflags & RTF_CACHE)
- flags[ifl++] = 'C';
- flags[ifl] = 0;
-
- /* Fetch and resolve the target address. */
- snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
- addr6p[0], addr6p[1], addr6p[2], addr6p[3],
- addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
- inet_pton(AF_INET6, addr6, (struct sockaddr *) &saddr6.sin6_addr);
- saddr6.sin6_family = AF_INET6;
-
- INET6_rresolve(addr6, sizeof(addr6), (struct sockaddr_in6 *) &saddr6,
- numeric);
- snprintf(addr6, sizeof(addr6), "%s/%d", addr6, prefix_len);
-
- /* Fetch and resolve the nexthop address. */
- snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s",
- naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
- naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]);
- inet_pton(AF_INET6, naddr6, (struct sockaddr *) &snaddr6.sin6_addr);
- snaddr6.sin6_family = AF_INET6;
-
- INET6_rresolve(naddr6, sizeof(naddr6),
- (struct sockaddr_in6 *) &snaddr6, numeric);
-
- /* Print the info. */
- printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
- addr6, naddr6, flags, metric, refcnt, use, iface);
+ }
+
+ set_flags(flags, (iflags & IPV6_MASK));
+
+ r = 0;
+ do {
+ inet_pton(AF_INET6, addr6x + r,
+ (struct sockaddr *) &snaddr6.sin6_addr);
+ snaddr6.sin6_family = AF_INET6;
+ naddr6 = INET6_rresolve((struct sockaddr_in6 *) &snaddr6,
+ 0x0fff /* Apparently, upstream never resolves. */
+ );
+
+ if (!r) { /* 1st pass */
+ snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
+ r += 40;
+ free(naddr6);
+ } else { /* 2nd pass */
+ /* Print the info. */
+ printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+ addr6, naddr6, flags, metric, refcnt, use, iface);
+ free(naddr6);
+ break;
+ }
+ } while (1);
}
}
+
#endif
-int route_main(int argc, char **argv)
+#define ROUTE_OPT_A 0x01
+#define ROUTE_OPT_n 0x02
+#define ROUTE_OPT_e 0x04
+#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
+
+/* 1st byte is offset to next entry offset. 2nd byte is return value. */
+/* 2nd byte matches RTACTION_* code */
+static const char tbl_verb[] ALIGN1 =
+ "\006\001add\0"
+ "\006\002del\0"
+/* "\011\002delete\0" */
+ "\010\002delete" /* Since it's last, we can save a byte. */
+;
+
+int route_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int route_main(int argc UNUSED_PARAM, char **argv)
{
- int opt;
- int what = 0;
-
-#ifdef CONFIG_FEATURE_IPV6
- int af = AF_INET;
-#endif
+ unsigned opt;
+ int what;
+ char *family;
+ char **p;
+
+ /* First, remap '-net' and '-host' to avoid getopt problems. */
+ p = argv;
+ while (*++p) {
+ if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
+ p[0][0] = '#';
+ }
+ }
- if (!argv[1] || (argv[1][0] == '-')) {
- /* check options */
- int noresolve = 0;
- int extended = 0;
+ opt = getopt32(argv, "A:ne", &family);
- while ((opt = getopt(argc, argv, "A:ne")) > 0) {
- switch (opt) {
- case 'n':
- noresolve = 1;
- break;
- case 'e':
- extended = 1;
- break;
- case 'A':
-#ifdef CONFIG_FEATURE_IPV6
- if (strcmp(optarg, "inet6") == 0)
- af = AF_INET6;
- break;
+ if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
+#if ENABLE_FEATURE_IPV6
+ if (strcmp(family, "inet6") == 0) {
+ opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
+ } else
#endif
- default:
- bb_show_usage();
- }
- }
+ bb_show_usage();
+ }
+
+ argv += optind;
-#ifdef CONFIG_FEATURE_IPV6
- if (af == AF_INET6)
- INET6_displayroutes(*argv != NULL);
+ /* No more args means display the routing table. */
+ if (!*argv) {
+ int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
+#if ENABLE_FEATURE_IPV6
+ if (opt & ROUTE_OPT_INET6)
+ INET6_displayroutes();
else
#endif
- displayroutes(noresolve, extended);
- return EXIT_SUCCESS;
- } else {
- /* check verb */
- if (strcmp(argv[1], "add") == 0)
- what = RTACTION_ADD;
- else if (strcmp(argv[1], "del") == 0
- || strcmp(argv[1], "delete") == 0)
- what = RTACTION_DEL;
- else if (strcmp(argv[1], "flush") == 0)
- what = RTACTION_FLUSH;
- else
- bb_show_usage();
+ bb_displayroutes(noresolve, opt & ROUTE_OPT_e);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ /* Check verb. At the moment, must be add, del, or delete. */
+ what = kw_lookup(tbl_verb, &argv);
+ if (!what || !*argv) { /* Unknown verb or no more args. */
+ bb_show_usage();
}
-#ifdef CONFIG_FEATURE_IPV6
- if (af == AF_INET6)
- return INET6_setroute(what, 0, argv + 2);
+#if ENABLE_FEATURE_IPV6
+ if (opt & ROUTE_OPT_INET6)
+ INET6_setroute(what, argv);
+ else
#endif
- return INET_setroute(what, 0, argv + 2);
+ INET_setroute(what, argv);
+
+ return EXIT_SUCCESS;
}
diff --git a/release/src/router/busybox/networking/slattach.c b/release/src/router/busybox/networking/slattach.c
new file mode 100644
index 00000000..d3212bb4
--- /dev/null
+++ b/release/src/router/busybox/networking/slattach.c
@@ -0,0 +1,245 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Stripped down version of net-tools for busybox.
+ *
+ * Author: Ignacio Garcia Perez (iggarpe at gmail dot com)
+ *
+ * License: GPLv2 or later, see LICENSE file in this tarball.
+ *
+ * There are some differences from the standard net-tools slattach:
+ *
+ * - The -l option is not supported.
+ *
+ * - The -F options allows disabling of RTS/CTS flow control.
+ */
+
+#include "libbb.h"
+#include "libiproute/utils.h" /* invarg() */
+
+struct globals {
+ int handle;
+ int saved_disc;
+ struct termios saved_state;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define handle (G.handle )
+#define saved_disc (G.saved_disc )
+#define saved_state (G.saved_state )
+#define INIT_G() do { } while (0)
+
+
+/*
+ * Save tty state and line discipline
+ *
+ * It is fine here to bail out on errors, since we haven modified anything yet
+ */
+static void save_state(void)
+{
+ /* Save line status */
+ if (tcgetattr(handle, &saved_state) < 0)
+ bb_perror_msg_and_die("get state");
+
+ /* Save line discipline */
+ xioctl(handle, TIOCGETD, &saved_disc);
+}
+
+static int set_termios_state_or_warn(struct termios *state)
+{
+ int ret;
+
+ ret = tcsetattr(handle, TCSANOW, state);
+ if (ret < 0) {
+ bb_perror_msg("set state");
+ return 1; /* used as exitcode */
+ }
+ return 0;
+}
+
+/*
+ * Restore state and line discipline for ALL managed ttys
+ *
+ * Restoring ALL managed ttys is the only way to have a single
+ * hangup delay.
+ *
+ * Go on after errors: we want to restore as many controlled ttys
+ * as possible.
+ */
+static void restore_state_and_exit(int exitcode) NORETURN;
+static void restore_state_and_exit(int exitcode)
+{
+ struct termios state;
+
+ /* Restore line discipline */
+ if (ioctl_or_warn(handle, TIOCSETD, &saved_disc) < 0) {
+ exitcode = 1;
+ }
+
+ /* Hangup */
+ memcpy(&state, &saved_state, sizeof(state));
+ cfsetispeed(&state, B0);
+ cfsetospeed(&state, B0);
+ if (set_termios_state_or_warn(&state))
+ exitcode = 1;
+ sleep(1);
+
+ /* Restore line status */
+ if (set_termios_state_or_warn(&saved_state))
+ exit(EXIT_FAILURE);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(handle);
+
+ exit(exitcode);
+}
+
+/*
+ * Set tty state, line discipline and encapsulation
+ */
+static void set_state(struct termios *state, int encap)
+{
+ int disc;
+
+ /* Set line status */
+ if (set_termios_state_or_warn(state))
+ goto bad;
+ /* Set line discliple (N_SLIP always) */
+ disc = N_SLIP;
+ if (ioctl_or_warn(handle, TIOCSETD, &disc) < 0) {
+ goto bad;
+ }
+
+ /* Set encapsulation (SLIP, CSLIP, etc) */
+ if (ioctl_or_warn(handle, SIOCSIFENCAP, &encap) < 0) {
+ bad:
+ restore_state_and_exit(EXIT_FAILURE);
+ }
+}
+
+static void sig_handler(int signo UNUSED_PARAM)
+{
+ restore_state_and_exit(EXIT_SUCCESS);
+}
+
+int slattach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int slattach_main(int argc UNUSED_PARAM, char **argv)
+{
+ /* Line discipline code table */
+ static const char proto_names[] ALIGN1 =
+ "slip\0" /* 0 */
+ "cslip\0" /* 1 */
+ "slip6\0" /* 2 */
+ "cslip6\0" /* 3 */
+ "adaptive\0" /* 8 */
+ ;
+
+ int i, encap, opt;
+ struct termios state;
+ const char *proto = "cslip";
+ const char *extcmd; /* Command to execute after hangup */
+ const char *baud_str;
+ int baud_code = -1; /* Line baud rate (system code) */
+
+ enum {
+ OPT_p_proto = 1 << 0,
+ OPT_s_baud = 1 << 1,
+ OPT_c_extcmd = 1 << 2,
+ OPT_e_quit = 1 << 3,
+ OPT_h_watch = 1 << 4,
+ OPT_m_nonraw = 1 << 5,
+ OPT_L_local = 1 << 6,
+ OPT_F_noflow = 1 << 7
+ };
+
+ INIT_G();
+
+ /* Parse command line options */
+ opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd);
+ /*argc -= optind;*/
+ argv += optind;
+
+ if (!*argv)
+ bb_show_usage();
+
+ encap = index_in_strings(proto_names, proto);
+
+ if (encap < 0)
+ invarg(proto, "protocol");
+ if (encap > 3)
+ encap = 8;
+
+ /* We want to know if the baud rate is valid before we start touching the ttys */
+ if (opt & OPT_s_baud) {
+ baud_code = tty_value_to_baud(xatoi(baud_str));
+ if (baud_code < 0)
+ invarg(baud_str, "baud rate");
+ }
+
+ /* Trap signals in order to restore tty states upon exit */
+ if (!(opt & OPT_e_quit)) {
+ bb_signals(0
+ + (1 << SIGHUP)
+ + (1 << SIGINT)
+ + (1 << SIGQUIT)
+ + (1 << SIGTERM)
+ , sig_handler);
+ }
+
+ /* Open tty */
+ handle = open(*argv, O_RDWR | O_NDELAY);
+ if (handle < 0) {
+ char *buf = concat_path_file("/dev", *argv);
+ handle = xopen(buf, O_RDWR | O_NDELAY);
+ /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
+ free(buf);
+ }
+
+ /* Save current tty state */
+ save_state();
+
+ /* Configure tty */
+ memcpy(&state, &saved_state, sizeof(state));
+ if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */
+ memset(&state.c_cc, 0, sizeof(state.c_cc));
+ state.c_cc[VMIN] = 1;
+ state.c_iflag = IGNBRK | IGNPAR;
+ state.c_oflag = 0;
+ state.c_lflag = 0;
+ state.c_cflag = CS8 | HUPCL | CREAD
+ | ((opt & OPT_L_local) ? CLOCAL : 0)
+ | ((opt & OPT_F_noflow) ? 0 : CRTSCTS);
+ cfsetispeed(&state, cfgetispeed(&saved_state));
+ cfsetospeed(&state, cfgetospeed(&saved_state));
+ }
+
+ if (opt & OPT_s_baud) {
+ cfsetispeed(&state, baud_code);
+ cfsetospeed(&state, baud_code);
+ }
+
+ set_state(&state, encap);
+
+ /* Exit now if option -e was passed */
+ if (opt & OPT_e_quit)
+ return 0;
+
+ /* If we're not requested to watch, just keep descriptor open
+ * until we are killed */
+ if (!(opt & OPT_h_watch))
+ while (1)
+ sleep(24*60*60);
+
+ /* Watch line for hangup */
+ while (1) {
+ if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR))
+ goto no_carrier;
+ sleep(15);
+ }
+
+ no_carrier:
+
+ /* Execute command on hangup */
+ if (opt & OPT_c_extcmd)
+ system(extcmd);
+
+ /* Restore states and exit */
+ restore_state_and_exit(EXIT_SUCCESS);
+}
diff --git a/release/src/router/busybox/networking/tc.c b/release/src/router/busybox/networking/tc.c
new file mode 100644
index 00000000..03f57f63
--- /dev/null
+++ b/release/src/router/busybox/networking/tc.c
@@ -0,0 +1,543 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tc.c "tc" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Bernhard Reutner-Fischer adjusted for busybox
+ */
+
+#include "libbb.h"
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+#include "libiproute/rt_names.h"
+#include <linux/pkt_sched.h> /* for the TC_H_* macros */
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+/* nullifies tb on error */
+#define __parse_rtattr_nested_compat(tb, max, rta, len) \
+ ({if ((RTA_PAYLOAD(rta) >= len) && \
+ (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr))) { \
+ rta = RTA_DATA(rta) + RTA_ALIGN(len); \
+ parse_rtattr_nested(tb, max, rta); \
+ } else \
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); \
+ })
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+ ({data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+ __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+#define show_details (0) /* not implemented. Does anyone need it? */
+#define use_iec (0) /* not currently documented in the upstream manpage */
+
+
+struct globals {
+ int filter_ifindex;
+ __u32 filter_qdisc;
+ __u32 filter_parent;
+ __u32 filter_prio;
+ __u32 filter_proto;
+};
+
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define filter_ifindex (G.filter_ifindex)
+#define filter_qdisc (G.filter_qdisc)
+#define filter_parent (G.filter_parent)
+#define filter_prio (G.filter_prio)
+#define filter_proto (G.filter_proto)
+
+void BUG_tc_globals_too_big(void);
+#define INIT_G() do { \
+ if (sizeof(G) > COMMON_BUFSIZE) \
+ BUG_tc_globals_too_big(); \
+} while (0)
+
+/* Allocates a buffer containing the name of a class id.
+ * The caller must free the returned memory. */
+static char* print_tc_classid(uint32_t cid)
+{
+#if 0 /* IMPOSSIBLE */
+ if (cid == TC_H_ROOT)
+ return xasprintf("root");
+ else
+#endif
+ if (cid == TC_H_UNSPEC)
+ return xasprintf("none");
+ else if (TC_H_MAJ(cid) == 0)
+ return xasprintf(":%x", TC_H_MIN(cid));
+ else if (TC_H_MIN(cid) == 0)
+ return xasprintf("%x:", TC_H_MAJ(cid)>>16);
+ else
+ return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid));
+}
+
+/* Get a qdisc handle. Return 0 on success, !0 otherwise. */
+static int get_qdisc_handle(__u32 *h, const char *str) {
+ __u32 maj;
+ char *p;
+
+ maj = TC_H_UNSPEC;
+ if (!strcmp(str, "none"))
+ goto ok;
+ maj = strtoul(str, &p, 16);
+ if (p == str)
+ return 1;
+ maj <<= 16;
+ if (*p != ':' && *p!=0)
+ return 1;
+ ok:
+ *h = maj;
+ return 0;
+}
+
+/* Get class ID. Return 0 on success, !0 otherwise. */
+static int get_tc_classid(__u32 *h, const char *str) {
+ __u32 maj, min;
+ char *p;
+
+ maj = TC_H_ROOT;
+ if (!strcmp(str, "root"))
+ goto ok;
+ maj = TC_H_UNSPEC;
+ if (!strcmp(str, "none"))
+ goto ok;
+ maj = strtoul(str, &p, 16);
+ if (p == str) {
+ if (*p != ':')
+ return 1;
+ maj = 0;
+ }
+ if (*p == ':') {
+ if (maj >= (1<<16))
+ return 1;
+ maj <<= 16;
+ str = p + 1;
+ min = strtoul(str, &p, 16);
+ if (*p != 0 || min >= (1<<16))
+ return 1;
+ maj |= min;
+ } else if (*p != 0)
+ return 1;
+ ok:
+ *h = maj;
+ return 0;
+}
+
+static void print_rate(char *buf, int len, uint32_t rate)
+{
+ double tmp = (double)rate*8;
+
+ if (use_iec) {
+ if (tmp >= 1000.0*1024.0*1024.0)
+ snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0);
+ else if (tmp >= 1000.0*1024)
+ snprintf(buf, len, "%.0fKibit", tmp/1024);
+ else
+ snprintf(buf, len, "%.0fbit", tmp);
+ } else {
+ if (tmp >= 1000.0*1000000.0)
+ snprintf(buf, len, "%.0fMbit", tmp/1000000.0);
+ else if (tmp >= 1000.0 * 1000.0)
+ snprintf(buf, len, "%.0fKbit", tmp/1000.0);
+ else
+ snprintf(buf, len, "%.0fbit", tmp);
+ }
+}
+
+/* This is "pfifo_fast". */
+static int prio_parse_opt(int argc, char **argv, struct nlmsghdr *n)
+{
+ return 0;
+}
+static int prio_print_opt(struct rtattr *opt)
+{
+ int i;
+ struct tc_prio_qopt *qopt;
+ struct rtattr *tb[TCA_PRIO_MAX+1];
+
+ if (opt == NULL)
+ return 0;
+ parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt));
+ if (tb == NULL)
+ return 0;
+ printf("bands %u priomap ", qopt->bands);
+ for (i=0; i<=TC_PRIO_MAX; i++)
+ printf(" %d", qopt->priomap[i]);
+
+ if (tb[TCA_PRIO_MQ])
+ printf(" multiqueue: o%s ",
+ *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "n" : "ff");
+
+ return 0;
+}
+
+/* Class Based Queue */
+static int cbq_parse_opt(int argc, char **argv, struct nlmsghdr *n)
+{
+ return 0;
+}
+static int cbq_print_opt(struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_CBQ_MAX+1];
+ struct tc_ratespec *r = NULL;
+ struct tc_cbq_lssopt *lss = NULL;
+ struct tc_cbq_wrropt *wrr = NULL;
+ struct tc_cbq_fopt *fopt = NULL;
+ struct tc_cbq_ovl *ovl = NULL;
+ const char * const error = "CBQ: too short %s opt";
+ RESERVE_CONFIG_BUFFER(buf, 64);
+
+ if (opt == NULL)
+ goto done;
+ parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
+
+ if (tb[TCA_CBQ_RATE]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
+ bb_error_msg(error, "rate");
+ else
+ r = RTA_DATA(tb[TCA_CBQ_RATE]);
+ }
+ if (tb[TCA_CBQ_LSSOPT]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
+ bb_error_msg(error, "lss");
+ else
+ lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
+ }
+ if (tb[TCA_CBQ_WRROPT]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
+ bb_error_msg(error, "wrr");
+ else
+ wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
+ }
+ if (tb[TCA_CBQ_FOPT]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
+ bb_error_msg(error, "fopt");
+ else
+ fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
+ }
+ if (tb[TCA_CBQ_OVL_STRATEGY]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
+ bb_error_msg("CBQ: too short overlimit strategy %u/%u",
+ (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
+ (unsigned) sizeof(*ovl));
+ else
+ ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
+ }
+
+ if (r) {
+ print_rate(buf, sizeof(buf), r->rate);
+ printf("rate %s ", buf);
+ if (show_details) {
+ printf("cell %ub ", 1<<r->cell_log);
+ if (r->mpu)
+ printf("mpu %ub ", r->mpu);
+ if (r->overhead)
+ printf("overhead %ub ", r->overhead);
+ }
+ }
+ if (lss && lss->flags) {
+ bool comma = false;
+ bb_putchar('(');
+ if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
+ printf("bounded");
+ comma = true;
+ }
+ if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
+ if (comma)
+ bb_putchar(',');
+ printf("isolated");
+ }
+ printf(") ");
+ }
+ if (wrr) {
+ if (wrr->priority != TC_CBQ_MAXPRIO)
+ printf("prio %u", wrr->priority);
+ else
+ printf("prio no-transmit");
+ if (show_details) {
+ printf("/%u ", wrr->cpriority);
+ if (wrr->weight != 1) {
+ print_rate(buf, sizeof(buf), wrr->weight);
+ printf("weight %s ", buf);
+ }
+ if (wrr->allot)
+ printf("allot %ub ", wrr->allot);
+ }
+ }
+ done:
+ RELEASE_CONFIG_BUFFER(buf);
+ return 0;
+}
+
+static int print_qdisc(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
+{
+ struct tcmsg *msg = NLMSG_DATA(hdr);
+ int len = hdr->nlmsg_len;
+ struct rtattr * tb[TCA_MAX+1];
+ char *name;
+
+ if (hdr->nlmsg_type != RTM_NEWQDISC && hdr->nlmsg_type != RTM_DELQDISC) {
+ /* bb_error_msg("Not a qdisc"); */
+ return 0; /* ??? mimic upstream; should perhaps return -1 */
+ }
+ len -= NLMSG_LENGTH(sizeof(*msg));
+ if (len < 0) {
+ /* bb_error_msg("Wrong len %d", len); */
+ return -1;
+ }
+ /* not the desired interface? */
+ if (filter_ifindex && filter_ifindex != msg->tcm_ifindex)
+ return 0;
+ memset (tb, 0, sizeof(tb));
+ parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len);
+ if (tb[TCA_KIND] == NULL) {
+ /* bb_error_msg("%s: NULL kind", "qdisc"); */
+ return -1;
+ }
+ if (hdr->nlmsg_type == RTM_DELQDISC)
+ printf("deleted ");
+ name = (char*)RTA_DATA(tb[TCA_KIND]);
+ printf("qdisc %s %x: ", name, msg->tcm_handle>>16);
+ if (filter_ifindex == 0)
+ printf("dev %s ", ll_index_to_name(msg->tcm_ifindex));
+ if (msg->tcm_parent == TC_H_ROOT)
+ printf("root ");
+ else if (msg->tcm_parent) {
+ char *classid = print_tc_classid(msg->tcm_parent);
+ printf("parent %s ", classid);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(classid);
+ }
+ if (msg->tcm_info != 1)
+ printf("refcnt %d ", msg->tcm_info);
+ if (tb[TCA_OPTIONS]) {
+ static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0";
+ int qqq = index_in_strings(_q_, name);
+ if (qqq == 0) { /* pfifo_fast aka prio */
+ prio_print_opt(tb[TCA_OPTIONS]);
+ } else if (qqq == 1) { /* class based queueing */
+ cbq_print_opt(tb[TCA_OPTIONS]);
+ } else
+ bb_error_msg("unknown %s", name);
+ }
+ bb_putchar('\n');
+ return 0;
+}
+
+static int print_class(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
+{
+ struct tcmsg *msg = NLMSG_DATA(hdr);
+ int len = hdr->nlmsg_len;
+ struct rtattr * tb[TCA_MAX+1];
+ char *name, *classid;
+
+ /*XXX Eventually factor out common code */
+
+ if (hdr->nlmsg_type != RTM_NEWTCLASS && hdr->nlmsg_type != RTM_DELTCLASS) {
+ /* bb_error_msg("Not a class"); */
+ return 0; /* ??? mimic upstream; should perhaps return -1 */
+ }
+ len -= NLMSG_LENGTH(sizeof(*msg));
+ if (len < 0) {
+ /* bb_error_msg("Wrong len %d", len); */
+ return -1;
+ }
+ /* not the desired interface? */
+ if (filter_qdisc && TC_H_MAJ(msg->tcm_handle^filter_qdisc))
+ return 0;
+ memset (tb, 0, sizeof(tb));
+ parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len);
+ if (tb[TCA_KIND] == NULL) {
+ /* bb_error_msg("%s: NULL kind", "class"); */
+ return -1;
+ }
+ if (hdr->nlmsg_type == RTM_DELTCLASS)
+ printf("deleted ");
+
+ name = (char*)RTA_DATA(tb[TCA_KIND]);
+ classid = !msg->tcm_handle ? NULL : print_tc_classid(
+ filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
+ printf ("class %s %s", name, classid);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(classid);
+
+ if (filter_ifindex == 0)
+ printf("dev %s ", ll_index_to_name(msg->tcm_ifindex));
+ if (msg->tcm_parent == TC_H_ROOT)
+ printf("root ");
+ else if (msg->tcm_parent) {
+ classid = print_tc_classid(filter_qdisc ?
+ TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
+ printf("parent %s ", classid);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(classid);
+ }
+ if (msg->tcm_info)
+ printf("leaf %x ", msg->tcm_info >> 16);
+ /* Do that get_qdisc_kind(RTA_DATA(tb[TCA_KIND])). */
+ if (tb[TCA_OPTIONS]) {
+ static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0";
+ int qqq = index_in_strings(_q_, name);
+ if (qqq == 0) { /* pfifo_fast aka prio */
+ /* nothing. */ /*prio_print_opt(tb[TCA_OPTIONS]);*/
+ } else if (qqq == 1) { /* class based queueing */
+ /* cbq_print_copt() is identical to cbq_print_opt(). */
+ cbq_print_opt(tb[TCA_OPTIONS]);
+ } else
+ bb_error_msg("unknown %s", name);
+ }
+ bb_putchar('\n');
+
+ return 0;
+}
+
+static int print_filter(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
+{
+ struct tcmsg *msg = NLMSG_DATA(hdr);
+ int len = hdr->nlmsg_len;
+ struct rtattr * tb[TCA_MAX+1];
+ return 0;
+}
+
+int tc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tc_main(int argc UNUSED_PARAM, char **argv)
+{
+ static const char objects[] ALIGN1 =
+ "qdisc\0""class\0""filter\0"
+ ;
+ enum { OBJ_qdisc = 0, OBJ_class, OBJ_filter };
+ static const char commands[] ALIGN1 =
+ "add\0""delete\0""change\0"
+ "link\0" /* only qdisc */
+ "replace\0"
+ "show\0""list\0"
+ ;
+ static const char args[] ALIGN1 =
+ "dev\0" /* qdisc, class, filter */
+ "root\0" /* class, filter */
+ "parent\0" /* class, filter */
+ "qdisc\0" /* class */
+ "handle\0" /* change: qdisc, class(classid) list: filter */
+ "classid\0" /* change: for class use "handle" */
+ "preference\0""priority\0""protocol\0" /* filter */
+ ;
+ enum { CMD_add = 0, CMD_del, CMD_change, CMD_link, CMD_replace, CMD_show };
+ enum { ARG_dev = 0, ARG_root, ARG_parent, ARG_qdisc,
+ ARG_handle, ARG_classid, ARG_pref, ARG_prio, ARG_proto};
+ struct rtnl_handle rth;
+ struct tcmsg msg;
+ int ret, obj, cmd, arg;
+ char *dev = NULL;
+
+ INIT_G();
+
+ if (!*++argv)
+ bb_show_usage();
+ xrtnl_open(&rth);
+ ret = EXIT_SUCCESS;
+
+ obj = index_in_substrings(objects, *argv++);
+
+ if (obj < OBJ_qdisc)
+ bb_show_usage();
+ if (!*argv)
+ cmd = CMD_show; /* list is the default */
+ else {
+ cmd = index_in_substrings(commands, *argv);
+ if (cmd < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ argv++;
+ }
+ memset(&msg, 0, sizeof(msg));
+ msg.tcm_family = AF_UNSPEC;
+ ll_init_map(&rth);
+ while (*argv) {
+ arg = index_in_substrings(args, *argv);
+ if (arg == ARG_dev) {
+ NEXT_ARG();
+ if (dev)
+ duparg2("dev", *argv);
+ dev = *argv++;
+ msg.tcm_ifindex = xll_name_to_index(dev);
+ if (cmd >= CMD_show)
+ filter_ifindex = msg.tcm_ifindex;
+ } else if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show)
+ || (arg == ARG_handle && obj == OBJ_qdisc
+ && cmd == CMD_change)) {
+ NEXT_ARG();
+ /* We don't care about duparg2("qdisc handle",*argv) for now */
+ if (get_qdisc_handle(&filter_qdisc, *argv))
+ invarg(*argv, "qdisc");
+ } else if (obj != OBJ_qdisc &&
+ (arg == ARG_root
+ || arg == ARG_parent
+ || (obj == OBJ_filter && arg >= ARG_pref))) {
+ } else {
+ invarg(*argv, "command");
+ }
+ NEXT_ARG();
+ if (arg == ARG_root) {
+ if (msg.tcm_parent)
+ duparg("parent", *argv);
+ msg.tcm_parent = TC_H_ROOT;
+ if (obj == OBJ_filter)
+ filter_parent = TC_H_ROOT;
+ } else if (arg == ARG_parent) {
+ __u32 handle;
+ if (msg.tcm_parent)
+ duparg(*argv, "parent");
+ if (get_tc_classid(&handle, *argv))
+ invarg(*argv, "parent");
+ msg.tcm_parent = handle;
+ if (obj == OBJ_filter)
+ filter_parent = handle;
+ } else if (arg == ARG_handle) { /* filter::list */
+ if (msg.tcm_handle)
+ duparg(*argv, "handle");
+ /* reject LONG_MIN || LONG_MAX */
+ /* TODO: for fw
+ if ((slash = strchr(handle, '/')) != NULL)
+ *slash = '\0';
+ */
+ msg.tcm_handle = get_u32(*argv, "handle");
+ /* if (slash) {if (get_u32(__u32 &mask, slash+1, NULL)) inv mask; addattr32(n, MAX_MSG, TCA_FW_MASK, mask); */
+ } else if (arg == ARG_classid && obj == OBJ_class && cmd == CMD_change){
+ } else if (arg == ARG_pref || arg == ARG_prio) { /* filter::list */
+ if (filter_prio)
+ duparg(*argv, "priority");
+ filter_prio = get_u32(*argv, "priority");
+ } else if (arg == ARG_proto) { /* filter::list */
+ __u16 tmp;
+ if (filter_proto)
+ duparg(*argv, "protocol");
+ if (ll_proto_a2n(&tmp, *argv))
+ invarg(*argv, "protocol");
+ filter_proto = tmp;
+ }
+ }
+ if (cmd >= CMD_show) { /* show or list */
+ if (obj == OBJ_filter)
+ msg.tcm_info = TC_H_MAKE(filter_prio<<16, filter_proto);
+ if (rtnl_dump_request(&rth, obj == OBJ_qdisc ? RTM_GETQDISC :
+ obj == OBJ_class ? RTM_GETTCLASS : RTM_GETTFILTER,
+ &msg, sizeof(msg)) < 0)
+ bb_simple_perror_msg_and_die("cannot send dump request");
+
+ xrtnl_dump_filter(&rth, obj == OBJ_qdisc ? print_qdisc :
+ obj == OBJ_class ? print_class : print_filter,
+ NULL);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ rtnl_close(&rth);
+ }
+ return ret;
+}
diff --git a/release/src/router/busybox/networking/tcpudp.c b/release/src/router/busybox/networking/tcpudp.c
new file mode 100644
index 00000000..55a3e089
--- /dev/null
+++ b/release/src/router/busybox/networking/tcpudp.c
@@ -0,0 +1,607 @@
+/* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
+ * which are released into public domain by the author.
+ * Homepage: http://smarden.sunsite.dk/ipsvd/
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+/* Based on ipsvd-0.12.1. This tcpsvd accepts all options
+ * which are supported by one from ipsvd-0.12.1, but not all are
+ * functional. See help text at the end of this file for details.
+ *
+ * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused.
+ *
+ * Busybox version exports TCPLOCALADDR instead of
+ * TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality
+ * (which is "struct sockaddr_XXX". Port is not a separate entity,
+ * it's just a part of (AF_INET[6]) sockaddr!).
+ *
+ * TCPORIGDSTADDR is Busybox-specific addition.
+ *
+ * udp server is hacked up by reusing TCP code. It has the following
+ * limitation inherent in Unix DGRAM sockets implementation:
+ * - local IP address is retrieved (using recvmsg voodoo) but
+ * child's socket is not bound to it (bind cannot be called on
+ * already bound socket). Thus it still can emit outgoing packets
+ * with wrong source IP...
+ * - don't know how to retrieve ORIGDST for udp.
+ */
+
+#include "libbb.h"
+/* Wants <limits.h> etc, thus included after libbb.h: */
+#include <linux/types.h> /* for __be32 etc */
+#include <linux/netfilter_ipv4.h>
+
+// TODO: move into this file:
+#include "tcpudp_perhost.h"
+
+#ifdef SSLSVD
+#include "matrixSsl.h"
+#include "ssl_io.h"
+#endif
+
+struct globals {
+ unsigned verbose;
+ unsigned max_per_host;
+ unsigned cur_per_host;
+ unsigned cnum;
+ unsigned cmax;
+ char **env_cur;
+ char *env_var[1]; /* actually bigger */
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define verbose (G.verbose )
+#define max_per_host (G.max_per_host)
+#define cur_per_host (G.cur_per_host)
+#define cnum (G.cnum )
+#define cmax (G.cmax )
+#define env_cur (G.env_cur )
+#define env_var (G.env_var )
+#define INIT_G() do { \
+ cmax = 30; \
+ env_cur = &env_var[0]; \
+} while (0)
+
+
+/* We have to be careful about leaking memory in repeated setenv's */
+static void xsetenv_plain(const char *n, const char *v)
+{
+ char *var = xasprintf("%s=%s", n, v);
+ *env_cur++ = var;
+ putenv(var);
+}
+
+static void xsetenv_proto(const char *proto, const char *n, const char *v)
+{
+ char *var = xasprintf("%s%s=%s", proto, n, v);
+ *env_cur++ = var;
+ putenv(var);
+}
+
+static void undo_xsetenv(void)
+{
+ char **pp = env_cur = &env_var[0];
+ while (*pp) {
+ char *var = *pp;
+ bb_unsetenv(var);
+ free(var);
+ *pp++ = NULL;
+ }
+}
+
+static void sig_term_handler(int sig)
+{
+ if (verbose)
+ bb_error_msg("got signal %u, exit", sig);
+ kill_myself_with_sig(sig);
+}
+
+/* Little bloated, but tries to give accurate info how child exited.
+ * Makes easier to spot segfaulting children etc... */
+static void print_waitstat(unsigned pid, int wstat)
+{
+ unsigned e = 0;
+ const char *cause = "?exit";
+
+ if (WIFEXITED(wstat)) {
+ cause++;
+ e = WEXITSTATUS(wstat);
+ } else if (WIFSIGNALED(wstat)) {
+ cause = "signal";
+ e = WTERMSIG(wstat);
+ }
+ bb_error_msg("end %d %s %d", pid, cause, e);
+}
+
+/* Must match getopt32 in main! */
+enum {
+ OPT_c = (1 << 0),
+ OPT_C = (1 << 1),
+ OPT_i = (1 << 2),
+ OPT_x = (1 << 3),
+ OPT_u = (1 << 4),
+ OPT_l = (1 << 5),
+ OPT_E = (1 << 6),
+ OPT_b = (1 << 7),
+ OPT_h = (1 << 8),
+ OPT_p = (1 << 9),
+ OPT_t = (1 << 10),
+ OPT_v = (1 << 11),
+ OPT_V = (1 << 12),
+ OPT_U = (1 << 13), /* from here: sslsvd only */
+ OPT_slash = (1 << 14),
+ OPT_Z = (1 << 15),
+ OPT_K = (1 << 16),
+};
+
+static void connection_status(void)
+{
+ /* "only 1 client max" desn't need this */
+ if (cmax > 1)
+ bb_error_msg("status %u/%u", cnum, cmax);
+}
+
+static void sig_child_handler(int sig UNUSED_PARAM)
+{
+ int wstat;
+ pid_t pid;
+
+ while ((pid = wait_any_nohang(&wstat)) > 0) {
+ if (max_per_host)
+ ipsvd_perhost_remove(pid);
+ if (cnum)
+ cnum--;
+ if (verbose)
+ print_waitstat(pid, wstat);
+ }
+ if (verbose)
+ connection_status();
+}
+
+int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
+{
+ char *str_C, *str_t;
+ char *user;
+ struct hcc *hccp;
+ const char *instructs;
+ char *msg_per_host = NULL;
+ unsigned len_per_host = len_per_host; /* gcc */
+#ifndef SSLSVD
+ struct bb_uidgid_t ugid;
+#endif
+ bool tcp;
+ uint16_t local_port;
+ char *preset_local_hostname = NULL;
+ char *remote_hostname = remote_hostname; /* for compiler */
+ char *remote_addr = remote_addr; /* for compiler */
+ len_and_sockaddr *lsa;
+ len_and_sockaddr local, remote;
+ socklen_t sa_len;
+ int pid;
+ int sock;
+ int conn;
+ unsigned backlog = 20;
+
+ INIT_G();
+
+ tcp = (applet_name[0] == 't');
+
+ /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
+ opt_complementary = "-3:i--i:ph:vv:b+:c+";
+#ifdef SSLSVD
+ getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
+ &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
+ &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
+ );
+#else
+ /* "+": stop on first non-option */
+ getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
+ &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
+ &backlog, &str_t, &verbose
+ );
+#endif
+ if (option_mask32 & OPT_C) { /* -C n[:message] */
+ max_per_host = bb_strtou(str_C, &str_C, 10);
+ if (str_C[0]) {
+ if (str_C[0] != ':')
+ bb_show_usage();
+ msg_per_host = str_C + 1;
+ len_per_host = strlen(msg_per_host);
+ }
+ }
+ if (max_per_host > cmax)
+ max_per_host = cmax;
+ if (option_mask32 & OPT_u) {
+ xget_uidgid(&ugid, user);
+ }
+#ifdef SSLSVD
+ if (option_mask32 & OPT_U) ssluser = optarg;
+ if (option_mask32 & OPT_slash) root = optarg;
+ if (option_mask32 & OPT_Z) cert = optarg;
+ if (option_mask32 & OPT_K) key = optarg;
+#endif
+ argv += optind;
+ if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
+ argv[0] = (char*)"0.0.0.0";
+
+ /* Per-IP flood protection is not thought-out for UDP */
+ if (!tcp)
+ max_per_host = 0;
+
+ bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
+
+#ifdef SSLSVD
+ sslser = user;
+ client = 0;
+ if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
+ xfunc_exitcode = 100;
+ bb_error_msg_and_die("-U ssluser must be set when running as root");
+ }
+ if (option_mask32 & OPT_u)
+ if (!uidgid_get(&sslugid, ssluser, 1)) {
+ if (errno) {
+ bb_perror_msg_and_die("can't get user/group: %s", ssluser);
+ }
+ bb_error_msg_and_die("unknown user/group %s", ssluser);
+ }
+ if (!cert) cert = "./cert.pem";
+ if (!key) key = cert;
+ if (matrixSslOpen() < 0)
+ fatal("cannot initialize ssl");
+ if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
+ if (client)
+ fatal("cannot read cert, key, or ca file");
+ fatal("cannot read cert or key file");
+ }
+ if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
+ fatal("cannot create ssl session");
+#endif
+
+ sig_block(SIGCHLD);
+ signal(SIGCHLD, sig_child_handler);
+ bb_signals(BB_FATAL_SIGS, sig_term_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (max_per_host)
+ ipsvd_perhost_init(cmax);
+
+ local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
+ lsa = xhost2sockaddr(argv[0], local_port);
+ argv += 2;
+
+ sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
+ setsockopt_reuseaddr(sock);
+ sa_len = lsa->len; /* I presume sockaddr len stays the same */
+ xbind(sock, &lsa->u.sa, sa_len);
+ if (tcp)
+ xlisten(sock, backlog);
+ else /* udp: needed for recv_from_to to work: */
+ socket_want_pktinfo(sock);
+ /* ndelay_off(sock); - it is the default I think? */
+
+#ifndef SSLSVD
+ if (option_mask32 & OPT_u) {
+ /* drop permissions */
+ xsetgid(ugid.gid);
+ xsetuid(ugid.uid);
+ }
+#endif
+
+ if (verbose) {
+ char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
+ bb_error_msg("listening on %s, starting", addr);
+ free(addr);
+#ifndef SSLSVD
+ if (option_mask32 & OPT_u)
+ printf(", uid %u, gid %u",
+ (unsigned)ugid.uid, (unsigned)ugid.gid);
+#endif
+ }
+
+ /* Main accept() loop */
+
+ again:
+ hccp = NULL;
+
+ while (cnum >= cmax)
+ wait_for_any_sig(); /* expecting SIGCHLD */
+
+ /* Accept a connection to fd #0 */
+ again1:
+ close(0);
+ again2:
+ sig_unblock(SIGCHLD);
+ local.len = remote.len = sa_len;
+ if (tcp) {
+ conn = accept(sock, &remote.u.sa, &remote.len);
+ } else {
+ /* In case recv_from_to won't be able to recover local addr.
+ * Also sets port - recv_from_to is unable to do it. */
+ local = *lsa;
+ conn = recv_from_to(sock, NULL, 0, MSG_PEEK,
+ &remote.u.sa, &local.u.sa, sa_len);
+ }
+ sig_block(SIGCHLD);
+ if (conn < 0) {
+ if (errno != EINTR)
+ bb_perror_msg(tcp ? "accept" : "recv");
+ goto again2;
+ }
+ xmove_fd(tcp ? conn : sock, 0);
+
+ if (max_per_host) {
+ /* Drop connection immediately if cur_per_host > max_per_host
+ * (minimizing load under SYN flood) */
+ remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
+ cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp);
+ if (cur_per_host > max_per_host) {
+ /* ipsvd_perhost_add detected that max is exceeded
+ * (and did not store ip in connection table) */
+ free(remote_addr);
+ if (msg_per_host) {
+ /* don't block or test for errors */
+ send(0, msg_per_host, len_per_host, MSG_DONTWAIT);
+ }
+ goto again1;
+ }
+ /* NB: remote_addr is not leaked, it is stored in conn table */
+ }
+
+ if (!tcp) {
+ /* Voodoo magic: making udp sockets each receive its own
+ * packets is not trivial, and I still not sure
+ * I do it 100% right.
+ * 1) we have to do it before fork()
+ * 2) order is important - is it right now? */
+
+ /* Open new non-connected UDP socket for further clients... */
+ sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+ setsockopt_reuseaddr(sock);
+ /* Make plain write/send work for old socket by supplying default
+ * destination address. This also restricts incoming packets
+ * to ones coming from this remote IP. */
+ xconnect(0, &remote.u.sa, sa_len);
+ /* hole? at this point we have no wildcard udp socket...
+ * can this cause clients to get "port unreachable" icmp?
+ * Yup, time window is very small, but it exists (is it?) */
+ /* ..."open new socket", continued */
+ xbind(sock, &lsa->u.sa, sa_len);
+ socket_want_pktinfo(sock);
+
+ /* Doesn't work:
+ * we cannot replace fd #0 - we will lose pending packet
+ * which is already buffered for us! And we cannot use fd #1
+ * instead - it will "intercept" all following packets, but child
+ * does not expect data coming *from fd #1*! */
+#if 0
+ /* Make it so that local addr is fixed to localp->u.sa
+ * and we don't accidentally accept packets to other local IPs. */
+ /* NB: we possibly bind to the _very_ same_ address & port as the one
+ * already bound in parent! This seems to work in Linux.
+ * (otherwise we can move socket to fd #0 only if bind succeeds) */
+ close(0);
+ set_nport(localp, htons(local_port));
+ xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0);
+ setsockopt_reuseaddr(0); /* crucial */
+ xbind(0, &localp->u.sa, localp->len);
+#endif
+ }
+
+ pid = vfork();
+ if (pid == -1) {
+ bb_perror_msg("vfork");
+ goto again;
+ }
+
+ if (pid != 0) {
+ /* Parent */
+ cnum++;
+ if (verbose)
+ connection_status();
+ if (hccp)
+ hccp->pid = pid;
+ /* clean up changes done by vforked child */
+ undo_xsetenv();
+ goto again;
+ }
+
+ /* Child: prepare env, log, and exec prog */
+
+ /* Closing tcp listening socket */
+ if (tcp)
+ close(sock);
+
+ { /* vfork alert! every xmalloc in this block should be freed! */
+ char *local_hostname = local_hostname; /* for compiler */
+ char *local_addr = NULL;
+ char *free_me0 = NULL;
+ char *free_me1 = NULL;
+ char *free_me2 = NULL;
+
+ if (verbose || !(option_mask32 & OPT_E)) {
+ if (!max_per_host) /* remote_addr is not yet known */
+ free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
+ if (option_mask32 & OPT_h) {
+ free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
+ if (!remote_hostname) {
+ bb_error_msg("cannot look up hostname for %s", remote_addr);
+ remote_hostname = remote_addr;
+ }
+ }
+ /* Find out local IP peer connected to.
+ * Errors ignored (I'm not paranoid enough to imagine kernel
+ * which doesn't know local IP). */
+ if (tcp)
+ getsockname(0, &local.u.sa, &local.len);
+ /* else: for UDP it is done earlier by parent */
+ local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
+ if (option_mask32 & OPT_h) {
+ local_hostname = preset_local_hostname;
+ if (!local_hostname) {
+ free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
+ if (!local_hostname)
+ bb_error_msg_and_die("cannot look up hostname for %s", local_addr);
+ }
+ /* else: local_hostname is not NULL, but is NOT malloced! */
+ }
+ }
+ if (verbose) {
+ pid = getpid();
+ if (max_per_host) {
+ bb_error_msg("concurrency %s %u/%u",
+ remote_addr,
+ cur_per_host, max_per_host);
+ }
+ bb_error_msg((option_mask32 & OPT_h)
+ ? "start %u %s-%s (%s-%s)"
+ : "start %u %s-%s",
+ pid,
+ local_addr, remote_addr,
+ local_hostname, remote_hostname);
+ }
+
+ if (!(option_mask32 & OPT_E)) {
+ /* setup ucspi env */
+ const char *proto = tcp ? "TCP" : "UDP";
+
+ /* Extract "original" destination addr:port
+ * from Linux firewall. Useful when you redirect
+ * an outbond connection to local handler, and it needs
+ * to know where it originally tried to connect */
+ if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) {
+ char *addr = xmalloc_sockaddr2dotted(&local.u.sa);
+ xsetenv_plain("TCPORIGDSTADDR", addr);
+ free(addr);
+ }
+ xsetenv_plain("PROTO", proto);
+ xsetenv_proto(proto, "LOCALADDR", local_addr);
+ xsetenv_proto(proto, "REMOTEADDR", remote_addr);
+ if (option_mask32 & OPT_h) {
+ xsetenv_proto(proto, "LOCALHOST", local_hostname);
+ xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
+ }
+ //compat? xsetenv_proto(proto, "REMOTEINFO", "");
+ /* additional */
+ if (cur_per_host > 0) /* can not be true for udp */
+ xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host));
+ }
+ free(local_addr);
+ free(free_me0);
+ free(free_me1);
+ free(free_me2);
+ }
+
+ xdup2(0, 1);
+
+ signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */
+ /* Non-ignored signals revert to SIG_DFL on exec anyway */
+ /*signal(SIGCHLD, SIG_DFL);*/
+ sig_unblock(SIGCHLD);
+
+#ifdef SSLSVD
+ strcpy(id, utoa(pid));
+ ssl_io(0, argv);
+#else
+ BB_EXECVP(argv[0], argv);
+#endif
+ bb_perror_msg_and_die("exec '%s'", argv[0]);
+}
+
+/*
+tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name]
+ [-i dir|-x cdb] [ -t sec] host port prog
+
+tcpsvd creates a TCP/IP socket, binds it to the address host:port,
+and listens on the socket for incoming connections.
+
+On each incoming connection, tcpsvd conditionally runs a program,
+with standard input reading from the socket, and standard output
+writing to the socket, to handle this connection. tcpsvd keeps
+listening on the socket for new connections, and can handle
+multiple connections simultaneously.
+
+tcpsvd optionally checks for special instructions depending
+on the IP address or hostname of the client that initiated
+the connection, see ipsvd-instruct(5).
+
+host
+ host either is a hostname, or a dotted-decimal IP address,
+ or 0. If host is 0, tcpsvd accepts connections to any local
+ IP address.
+ * busybox accepts IPv6 addresses and host:port pairs too
+ In this case second parameter is ignored
+port
+ tcpsvd accepts connections to host:port. port may be a name
+ from /etc/services or a number.
+prog
+ prog consists of one or more arguments. For each connection,
+ tcpsvd normally runs prog, with file descriptor 0 reading from
+ the network, and file descriptor 1 writing to the network.
+ By default it also sets up TCP-related environment variables,
+ see tcp-environ(5)
+-i dir
+ read instructions for handling new connections from the instructions
+ directory dir. See ipsvd-instruct(5) for details.
+ * ignored by busyboxed version
+-x cdb
+ read instructions for handling new connections from the constant database
+ cdb. The constant database normally is created from an instructions
+ directory by running ipsvd-cdb(8).
+ * ignored by busyboxed version
+-t sec
+ timeout. This option only takes effect if the -i option is given.
+ While checking the instructions directory, check the time of last access
+ of the file that matches the clients address or hostname if any, discard
+ and remove the file if it wasn't accessed within the last sec seconds;
+ tcpsvd does not discard or remove a file if the user's write permission
+ is not set, for those files the timeout is disabled. Default is 0,
+ which means that the timeout is disabled.
+ * ignored by busyboxed version
+-l name
+ local hostname. Do not look up the local hostname in DNS, but use name
+ as hostname. This option must be set if tcpsvd listens on port 53
+ to avoid loops.
+-u user[:group]
+ drop permissions. Switch user ID to user's UID, and group ID to user's
+ primary GID after creating and binding to the socket. If user is followed
+ by a colon and a group name, the group ID is switched to the GID of group
+ instead. All supplementary groups are removed.
+-c n
+ concurrency. Handle up to n connections simultaneously. Default is 30.
+ If there are n connections active, tcpsvd defers acceptance of a new
+ connection until an active connection is closed.
+-C n[:msg]
+ per host concurrency. Allow only up to n connections from the same IP
+ address simultaneously. If there are n active connections from one IP
+ address, new incoming connections from this IP address are closed
+ immediately. If n is followed by :msg, the message msg is written
+ to the client if possible, before closing the connection. By default
+ msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg.
+
+ For each accepted connection, the current per host concurrency is
+ available through the environment variable TCPCONCURRENCY. n and msg
+ can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5).
+ By default tcpsvd doesn't keep track of connections.
+-h
+ Look up the client's hostname in DNS.
+-p
+ paranoid. After looking up the client's hostname in DNS, look up the IP
+ addresses in DNS for that hostname, and forget about the hostname
+ if none of the addresses match the client's IP address. You should
+ set this option if you use hostname based instructions. The -p option
+ implies the -h option.
+ * ignored by busyboxed version
+-b n
+ backlog. Allow a backlog of approximately n TCP SYNs. On some systems n
+ is silently limited. Default is 20.
+-E
+ no special environment. Do not set up TCP-related environment variables.
+-v
+ verbose. Print verbose messsages to standard output.
+-vv
+ more verbose. Print more verbose messages to standard output.
+ * no difference between -v and -vv in busyboxed version
+*/
diff --git a/release/src/router/busybox/networking/tcpudp_perhost.c b/release/src/router/busybox/networking/tcpudp_perhost.c
new file mode 100644
index 00000000..3005f12c
--- /dev/null
+++ b/release/src/router/busybox/networking/tcpudp_perhost.c
@@ -0,0 +1,65 @@
+/* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
+ * which are released into public domain by the author.
+ * Homepage: http://smarden.sunsite.dk/ipsvd/
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "tcpudp_perhost.h"
+
+static struct hcc *cc;
+static unsigned cclen;
+
+/* to be optimized */
+
+void ipsvd_perhost_init(unsigned c)
+{
+// free(cc);
+ cc = xzalloc(c * sizeof(*cc));
+ cclen = c;
+}
+
+unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp)
+{
+ unsigned i;
+ unsigned conn = 1;
+ int freepos = -1;
+
+ for (i = 0; i < cclen; ++i) {
+ if (!cc[i].ip) {
+ freepos = i;
+ continue;
+ }
+ if (strcmp(cc[i].ip, ip) == 0) {
+ conn++;
+ continue;
+ }
+ }
+ if (freepos == -1) return 0;
+ if (conn <= maxconn) {
+ cc[freepos].ip = ip;
+ *hccpp = &cc[freepos];
+ }
+ return conn;
+}
+
+void ipsvd_perhost_remove(int pid)
+{
+ unsigned i;
+ for (i = 0; i < cclen; ++i) {
+ if (cc[i].pid == pid) {
+ free(cc[i].ip);
+ cc[i].ip = NULL;
+ cc[i].pid = 0;
+ return;
+ }
+ }
+}
+
+//void ipsvd_perhost_free(void)
+//{
+// free(cc);
+//}
diff --git a/release/src/router/busybox/networking/tcpudp_perhost.h b/release/src/router/busybox/networking/tcpudp_perhost.h
new file mode 100644
index 00000000..d370036a
--- /dev/null
+++ b/release/src/router/busybox/networking/tcpudp_perhost.h
@@ -0,0 +1,33 @@
+/* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
+ * which are released into public domain by the author.
+ * Homepage: http://smarden.sunsite.dk/ipsvd/
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+struct hcc {
+ char *ip;
+ int pid;
+};
+
+void ipsvd_perhost_init(unsigned);
+
+/* Returns number of already opened connects to this ips, including this one.
+ * ip should be a malloc'ed ptr.
+ * If return value is <= maxconn, ip is inserted into the table
+ * and pointer to table entry if stored in *hccpp
+ * (useful for storing pid later).
+ * Else ip is NOT inserted (you must take care of it - free() etc) */
+unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp);
+
+/* Finds and frees element with pid */
+void ipsvd_perhost_remove(int pid);
+
+//unsigned ipsvd_perhost_setpid(int pid);
+//void ipsvd_perhost_free(void);
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/release/src/router/busybox/networking/telnet.c b/release/src/router/busybox/networking/telnet.c
index 88607f65..cc994250 100644
--- a/release/src/router/busybox/networking/telnet.c
+++ b/release/src/router/busybox/networking/telnet.c
@@ -8,19 +8,7 @@
* Created: Thu Apr 7 13:29:41 1994 too
* Last modified: Fri Jun 9 14:34:24 2000 too
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* HISTORY
* Revision 3.1 1994/04/17 11:31:54 too
@@ -28,55 +16,32 @@
* Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
* Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
* <jam@ltsp.org>
+ * Modified 2004/02/11 to add ability to pass the USER variable to remote host
+ * by Fernando Silveira <swrh@gmx.net>
*
*/
-#include <termios.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <signal.h>
#include <arpa/telnet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
#include <netinet/in.h>
-#include "busybox.h"
-
-#ifdef CONFIG_FEATURE_AUTOWIDTH
-# include <sys/ioctl.h>
-#endif
-
-#if 0
-static const int DOTRACE = 1;
-#endif
+#include "libbb.h"
#ifdef DOTRACE
-#include <arpa/inet.h> /* for inet_ntoa()... */
#define TRACE(x, y) do { if (x) printf y; } while (0)
#else
-#define TRACE(x, y)
-#endif
-
-#if 0
-#define USE_POLL
-#include <sys/poll.h>
-#else
-#include <sys/time.h>
+#define TRACE(x, y)
#endif
-#define DATABUFSIZE 128
-#define IACBUFSIZE 128
+enum {
+ DATABUFSIZE = 128,
+ IACBUFSIZE = 128,
-static const int CHM_TRY = 0;
-static const int CHM_ON = 1;
-static const int CHM_OFF = 2;
+ CHM_TRY = 0,
+ CHM_ON = 1,
+ CHM_OFF = 2,
-static const int UF_ECHO = 0x01;
-static const int UF_SGA = 0x02;
+ UF_ECHO = 0x01,
+ UF_SGA = 0x02,
-enum {
TS_0 = 1,
TS_IAC = 2,
TS_OPT = 3,
@@ -84,39 +49,40 @@ enum {
TS_SUB2 = 5,
};
-#define WriteCS(fd, str) write(fd, str, sizeof str -1)
-
typedef unsigned char byte;
-/* use globals to reduce size ??? */ /* test this hypothesis later */
-static struct Globalvars {
- int netfd; /* console fd:s are 0 and 1 (and 2) */
- /* same buffer used both for network and console read/write */
- char buf[DATABUFSIZE]; /* allocating so static size is smaller */
+enum { netfd = 3 };
+
+struct globals {
+ int iaclen; /* could even use byte, but it's a loss on x86 */
byte telstate; /* telnet negotiation state from network input */
byte telwish; /* DO, DONT, WILL, WONT */
byte charmode;
byte telflags;
- byte gotsig;
+ byte do_termios;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ char *ttype;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ const char *autologin;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+ unsigned win_width, win_height;
+#endif
+ /* same buffer used both for network and console read/write */
+ char buf[DATABUFSIZE];
/* buffer to handle telnet negotiations */
char iacbuf[IACBUFSIZE];
- short iaclen; /* could even use byte */
- struct termios termios_def;
- struct termios termios_raw;
-} G;
-
-#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
-
-#ifdef USE_GLOBALVAR_PTR
-struct Globalvars * Gptr;
-#define G (*Gptr)
-#endif
-
-static inline void iacflush(void)
-{
- write(G.netfd, G.iacbuf, G.iaclen);
- G.iaclen = 0;
-}
+ struct termios termios_def;
+ struct termios termios_raw;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+void BUG_telnet_globals_too_big(void);
+#define INIT_G() do { \
+ if (sizeof(G) > COMMON_BUFSIZE) \
+ BUG_telnet_globals_too_big(); \
+ /* memset(&G, 0, sizeof G); - already is */ \
+} while (0)
/* Function prototypes */
static void rawmode(void);
@@ -126,53 +92,48 @@ static void will_charmode(void);
static void telopt(byte c);
static int subneg(byte c);
-/* Some globals */
-static int one = 1;
-
-#ifdef CONFIG_FEATURE_TELNET_TTYPE
-static char *ttype;
-#endif
+static void iac_flush(void)
+{
+ write(netfd, G.iacbuf, G.iaclen);
+ G.iaclen = 0;
+}
-#ifdef CONFIG_FEATURE_AUTOWIDTH
-static int win_width, win_height;
-#endif
+#define write_str(fd, str) write(fd, str, sizeof(str) - 1)
+static void doexit(int ev) NORETURN;
static void doexit(int ev)
{
cookmode();
exit(ev);
-}
+}
-static void conescape(void)
+static void con_escape(void)
{
char b;
- if (G.gotsig) /* came from line mode... go raw */
+ if (bb_got_signal) /* came from line mode... go raw */
rawmode();
- WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
+ write_str(1, "\r\nConsole escape. Commands are:\r\n\n"
" l go to line mode\r\n"
" c go to character mode\r\n"
" z suspend telnet\r\n"
" e exit telnet\r\n");
- if (read(0, &b, 1) <= 0)
- doexit(1);
+ if (read(STDIN_FILENO, &b, 1) <= 0)
+ doexit(EXIT_FAILURE);
- switch (b)
- {
+ switch (b) {
case 'l':
- if (!G.gotsig)
- {
+ if (!bb_got_signal) {
do_linemode();
- goto rrturn;
+ goto ret;
}
break;
case 'c':
- if (G.gotsig)
- {
+ if (bb_got_signal) {
will_charmode();
- goto rrturn;
+ goto ret;
}
break;
case 'z':
@@ -181,222 +142,219 @@ static void conescape(void)
rawmode();
break;
case 'e':
- doexit(0);
+ doexit(EXIT_SUCCESS);
}
- WriteCS(1, "continuing...\r\n");
+ write_str(1, "continuing...\r\n");
- if (G.gotsig)
+ if (bb_got_signal)
cookmode();
-
- rrturn:
- G.gotsig = 0;
-
+ ret:
+ bb_got_signal = 0;
+
}
-static void handlenetoutput(int len)
+
+static void handle_net_output(int len)
{
- /* here we could do smart tricks how to handle 0xFF:s in output
- * stream like writing twice every sequence of FF:s (thus doing
- * many write()s. But I think interactive telnet application does
- * not need to be 100% 8-bit clean, so changing every 0xff:s to
- * 0x7f:s
+ /* here we could do smart tricks how to handle 0xFF:s in output
+ * stream like writing twice every sequence of FF:s (thus doing
+ * many write()s. But I think interactive telnet application does
+ * not need to be 100% 8-bit clean, so changing every 0xff:s to
+ * 0x7f:s
*
- * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
- * I don't agree.
- * first - I cannot use programs like sz/rz
- * second - the 0x0D is sent as one character and if the next
- * char is 0x0A then it's eaten by a server side.
- * third - whay doy you have to make 'many write()s'?
- * I don't understand.
- * So I implemented it. It's realy useful for me. I hope that
- * others people will find it interesting to.
+ * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
+ * I don't agree.
+ * first - I cannot use programs like sz/rz
+ * second - the 0x0D is sent as one character and if the next
+ * char is 0x0A then it's eaten by a server side.
+ * third - why do you have to make 'many write()s'?
+ * I don't understand.
+ * So I implemented it. It's really useful for me. I hope that
+ * other people will find it interesting too.
*/
int i, j;
- byte * p = G.buf;
+ byte *p = (byte*)G.buf;
byte outbuf[4*DATABUFSIZE];
- for (i = len, j = 0; i > 0; i--, p++)
- {
- if (*p == 0x1d)
- {
- conescape();
+ for (i = len, j = 0; i > 0; i--, p++) {
+ if (*p == 0x1d) {
+ con_escape();
return;
}
outbuf[j++] = *p;
if (*p == 0xff)
- outbuf[j++] = 0xff;
+ outbuf[j++] = 0xff;
else if (*p == 0x0d)
- outbuf[j++] = 0x00;
+ outbuf[j++] = 0x00;
}
- if (j > 0 )
- write(G.netfd, outbuf, j);
+ if (j > 0)
+ write(netfd, outbuf, j);
}
-
-static void handlenetinput(int len)
+static void handle_net_input(int len)
{
int i;
int cstart = 0;
- for (i = 0; i < len; i++)
- {
+ for (i = 0; i < len; i++) {
byte c = G.buf[i];
- if (G.telstate == 0) /* most of the time state == 0 */
- {
- if (c == IAC)
- {
+ if (G.telstate == 0) { /* most of the time state == 0 */
+ if (c == IAC) {
cstart = i;
G.telstate = TS_IAC;
}
+ continue;
}
- else
- switch (G.telstate)
- {
- case TS_0:
- if (c == IAC)
- G.telstate = TS_IAC;
- else
- G.buf[cstart++] = c;
- break;
-
- case TS_IAC:
- if (c == IAC) /* IAC IAC -> 0xFF */
- {
- G.buf[cstart++] = c;
- G.telstate = TS_0;
- break;
- }
- /* else */
- switch (c)
- {
- case SB:
- G.telstate = TS_SUB1;
- break;
- case DO:
- case DONT:
- case WILL:
- case WONT:
- G.telwish = c;
- G.telstate = TS_OPT;
- break;
- default:
- G.telstate = TS_0; /* DATA MARK must be added later */
- }
- break;
- case TS_OPT: /* WILL, WONT, DO, DONT */
- telopt(c);
- G.telstate = TS_0;
- break;
- case TS_SUB1: /* Subnegotiation */
- case TS_SUB2: /* Subnegotiation */
- if (subneg(c))
- G.telstate = TS_0;
- break;
- }
- }
- if (G.telstate)
- {
- if (G.iaclen) iacflush();
- if (G.telstate == TS_0) G.telstate = 0;
+ switch (G.telstate) {
+ case TS_0:
+ if (c == IAC)
+ G.telstate = TS_IAC;
+ else
+ G.buf[cstart++] = c;
+ break;
+ case TS_IAC:
+ if (c == IAC) { /* IAC IAC -> 0xFF */
+ G.buf[cstart++] = c;
+ G.telstate = TS_0;
+ break;
+ }
+ /* else */
+ switch (c) {
+ case SB:
+ G.telstate = TS_SUB1;
+ break;
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ G.telwish = c;
+ G.telstate = TS_OPT;
+ break;
+ default:
+ G.telstate = TS_0; /* DATA MARK must be added later */
+ }
+ break;
+ case TS_OPT: /* WILL, WONT, DO, DONT */
+ telopt(c);
+ G.telstate = TS_0;
+ break;
+ case TS_SUB1: /* Subnegotiation */
+ case TS_SUB2: /* Subnegotiation */
+ if (subneg(c))
+ G.telstate = TS_0;
+ break;
+ }
+ }
+ if (G.telstate) {
+ if (G.iaclen)
+ iac_flush();
+ if (G.telstate == TS_0)
+ G.telstate = 0;
len = cstart;
}
if (len)
- write(1, G.buf, len);
+ write(STDOUT_FILENO, G.buf, len);
}
-
-/* ******************************* */
-
-static inline void putiac(int c)
+static void put_iac(int c)
{
G.iacbuf[G.iaclen++] = c;
}
-
-static void putiac2(byte wwdd, byte c)
+static void put_iac2(byte wwdd, byte c)
{
if (G.iaclen + 3 > IACBUFSIZE)
- iacflush();
+ iac_flush();
- putiac(IAC);
- putiac(wwdd);
- putiac(c);
+ put_iac(IAC);
+ put_iac(wwdd);
+ put_iac(c);
}
-#if 0
-static void putiac1(byte c)
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void put_iac_subopt(byte c, char *str)
{
- if (G.iaclen + 2 > IACBUFSIZE)
- iacflush();
+ int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
+
+ if (G.iaclen + len > IACBUFSIZE)
+ iac_flush();
+
+ put_iac(IAC);
+ put_iac(SB);
+ put_iac(c);
+ put_iac(0);
- putiac(IAC);
- putiac(c);
+ while (*str)
+ put_iac(*str++);
+
+ put_iac(IAC);
+ put_iac(SE);
}
#endif
-#ifdef CONFIG_FEATURE_TELNET_TTYPE
-static void putiac_subopt(byte c, char *str)
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void put_iac_subopt_autologin(void)
{
- int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
+ int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2)
+ const char *user = "USER";
if (G.iaclen + len > IACBUFSIZE)
- iacflush();
+ iac_flush();
+
+ put_iac(IAC);
+ put_iac(SB);
+ put_iac(TELOPT_NEW_ENVIRON);
+ put_iac(TELQUAL_IS);
+ put_iac(NEW_ENV_VAR);
- putiac(IAC);
- putiac(SB);
- putiac(c);
- putiac(0);
+ while (*user)
+ put_iac(*user++);
- while(*str)
- putiac(*str++);
+ put_iac(NEW_ENV_VALUE);
- putiac(IAC);
- putiac(SE);
+ while (*G.autologin)
+ put_iac(*G.autologin++);
+
+ put_iac(IAC);
+ put_iac(SE);
}
#endif
-#ifdef CONFIG_FEATURE_AUTOWIDTH
-static void putiac_naws(byte c, int x, int y)
+#if ENABLE_FEATURE_AUTOWIDTH
+static void put_iac_naws(byte c, int x, int y)
{
if (G.iaclen + 9 > IACBUFSIZE)
- iacflush();
+ iac_flush();
- putiac(IAC);
- putiac(SB);
- putiac(c);
+ put_iac(IAC);
+ put_iac(SB);
+ put_iac(c);
- putiac((x >> 8) & 0xff);
- putiac(x & 0xff);
- putiac((y >> 8) & 0xff);
- putiac(y & 0xff);
+ put_iac((x >> 8) & 0xff);
+ put_iac(x & 0xff);
+ put_iac((y >> 8) & 0xff);
+ put_iac(y & 0xff);
- putiac(IAC);
- putiac(SE);
+ put_iac(IAC);
+ put_iac(SE);
}
#endif
-/* void putiacstring (subneg strings) */
-
-/* ******************************* */
-
-static char const escapecharis[] = "\r\nEscape character is ";
+static char const escapecharis[] ALIGN1 = "\r\nEscape character is ";
static void setConMode(void)
{
- if (G.telflags & UF_ECHO)
- {
+ if (G.telflags & UF_ECHO) {
if (G.charmode == CHM_TRY) {
G.charmode = CHM_ON;
printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
rawmode();
}
- }
- else
- {
+ } else {
if (G.charmode != CHM_OFF) {
G.charmode = CHM_OFF;
printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
@@ -405,17 +363,15 @@ static void setConMode(void)
}
}
-/* ******************************* */
-
static void will_charmode(void)
{
G.charmode = CHM_TRY;
G.telflags |= (UF_ECHO | UF_SGA);
setConMode();
-
- putiac2(DO, TELOPT_ECHO);
- putiac2(DO, TELOPT_SGA);
- iacflush();
+
+ put_iac2(DO, TELOPT_ECHO);
+ put_iac2(DO, TELOPT_SGA);
+ iac_flush();
}
static void do_linemode(void)
@@ -424,125 +380,139 @@ static void do_linemode(void)
G.telflags &= ~(UF_ECHO | UF_SGA);
setConMode();
- putiac2(DONT, TELOPT_ECHO);
- putiac2(DONT, TELOPT_SGA);
- iacflush();
+ put_iac2(DONT, TELOPT_ECHO);
+ put_iac2(DONT, TELOPT_SGA);
+ iac_flush();
}
-/* ******************************* */
-
-static inline void to_notsup(char c)
+static void to_notsup(char c)
{
- if (G.telwish == WILL) putiac2(DONT, c);
- else if (G.telwish == DO) putiac2(WONT, c);
+ if (G.telwish == WILL)
+ put_iac2(DONT, c);
+ else if (G.telwish == DO)
+ put_iac2(WONT, c);
}
-static inline void to_echo(void)
+static void to_echo(void)
{
/* if server requests ECHO, don't agree */
- if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
- else if (G.telwish == DONT) return;
-
- if (G.telflags & UF_ECHO)
- {
- if (G.telwish == WILL)
- return;
+ if (G.telwish == DO) {
+ put_iac2(WONT, TELOPT_ECHO);
+ return;
}
- else
- if (G.telwish == WONT)
+ if (G.telwish == DONT)
+ return;
+
+ if (G.telflags & UF_ECHO) {
+ if (G.telwish == WILL)
return;
+ } else if (G.telwish == WONT)
+ return;
if (G.charmode != CHM_OFF)
G.telflags ^= UF_ECHO;
if (G.telflags & UF_ECHO)
- putiac2(DO, TELOPT_ECHO);
+ put_iac2(DO, TELOPT_ECHO);
else
- putiac2(DONT, TELOPT_ECHO);
+ put_iac2(DONT, TELOPT_ECHO);
setConMode();
- WriteCS(1, "\r\n"); /* sudden modec */
+ write_str(1, "\r\n"); /* sudden modec */
}
-static inline void to_sga(void)
+static void to_sga(void)
{
/* daemon always sends will/wont, client do/dont */
- if (G.telflags & UF_SGA)
- {
+ if (G.telflags & UF_SGA) {
if (G.telwish == WILL)
return;
- }
- else
- if (G.telwish == WONT)
- return;
-
- if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
- putiac2(DO, TELOPT_SGA);
- else
- putiac2(DONT, TELOPT_SGA);
+ } else if (G.telwish == WONT)
+ return;
- return;
+ G.telflags ^= UF_SGA; /* toggle */
+ if (G.telflags & UF_SGA)
+ put_iac2(DO, TELOPT_SGA);
+ else
+ put_iac2(DONT, TELOPT_SGA);
}
-#ifdef CONFIG_FEATURE_TELNET_TTYPE
-static inline void to_ttype(void)
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void to_ttype(void)
{
/* Tell server we will (or won't) do TTYPE */
- if(ttype)
- putiac2(WILL, TELOPT_TTYPE);
+ if (G.ttype)
+ put_iac2(WILL, TELOPT_TTYPE);
else
- putiac2(WONT, TELOPT_TTYPE);
+ put_iac2(WONT, TELOPT_TTYPE);
+}
+#endif
- return;
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void to_new_environ(void)
+{
+ /* Tell server we will (or will not) do AUTOLOGIN */
+
+ if (G.autologin)
+ put_iac2(WILL, TELOPT_NEW_ENVIRON);
+ else
+ put_iac2(WONT, TELOPT_NEW_ENVIRON);
}
#endif
-#ifdef CONFIG_FEATURE_AUTOWIDTH
-static inline void to_naws(void)
-{
+#if ENABLE_FEATURE_AUTOWIDTH
+static void to_naws(void)
+{
/* Tell server we will do NAWS */
- putiac2(WILL, TELOPT_NAWS);
- return;
-}
+ put_iac2(WILL, TELOPT_NAWS);
+}
#endif
static void telopt(byte c)
{
- switch (c)
- {
- case TELOPT_ECHO: to_echo(); break;
- case TELOPT_SGA: to_sga(); break;
-#ifdef CONFIG_FEATURE_TELNET_TTYPE
- case TELOPT_TTYPE: to_ttype();break;
+ switch (c) {
+ case TELOPT_ECHO:
+ to_echo(); break;
+ case TELOPT_SGA:
+ to_sga(); break;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ case TELOPT_TTYPE:
+ to_ttype(); break;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ case TELOPT_NEW_ENVIRON:
+ to_new_environ(); break;
#endif
-#ifdef CONFIG_FEATURE_AUTOWIDTH
- case TELOPT_NAWS: to_naws();
- putiac_naws(c, win_width, win_height);
- break;
+#if ENABLE_FEATURE_AUTOWIDTH
+ case TELOPT_NAWS:
+ to_naws();
+ put_iac_naws(c, G.win_width, G.win_height);
+ break;
#endif
- default: to_notsup(c);
- break;
+ default:
+ to_notsup(c);
+ break;
}
}
-
-/* ******************************* */
-
/* subnegotiation -- ignore all (except TTYPE,NAWS) */
-
static int subneg(byte c)
{
- switch (G.telstate)
- {
+ switch (G.telstate) {
case TS_SUB1:
if (c == IAC)
G.telstate = TS_SUB2;
-#ifdef CONFIG_FEATURE_TELNET_TTYPE
+#if ENABLE_FEATURE_TELNET_TTYPE
else
if (c == TELOPT_TTYPE)
- putiac_subopt(TELOPT_TTYPE,ttype);
+ put_iac_subopt(TELOPT_TTYPE, G.ttype);
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ else
+ if (c == TELOPT_NEW_ENVIRON)
+ put_iac_subopt_autologin();
#endif
break;
case TS_SUB2:
@@ -554,93 +524,95 @@ static int subneg(byte c)
return FALSE;
}
-/* ******************************* */
-
-static void fgotsig(int sig)
-{
- G.gotsig = sig;
-}
-
-
static void rawmode(void)
{
- tcsetattr(0, TCSADRAIN, &G.termios_raw);
-}
+ if (G.do_termios)
+ tcsetattr(0, TCSADRAIN, &G.termios_raw);
+}
static void cookmode(void)
{
- tcsetattr(0, TCSADRAIN, &G.termios_def);
+ if (G.do_termios)
+ tcsetattr(0, TCSADRAIN, &G.termios_def);
}
-extern int telnet_main(int argc, char** argv)
+/* poll gives smaller (-70 bytes) code */
+#define USE_POLL 1
+
+int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int telnet_main(int argc UNUSED_PARAM, char **argv)
{
char *host;
- char *port;
+ int port;
int len;
#ifdef USE_POLL
struct pollfd ufds[2];
-#else
+#else
fd_set readfds;
int maxfd;
-#endif
-
-#ifdef CONFIG_FEATURE_AUTOWIDTH
- struct winsize winp;
- if( ioctl(0, TIOCGWINSZ, &winp) == 0 ) {
- win_width = winp.ws_col;
- win_height = winp.ws_row;
- }
#endif
-#ifdef CONFIG_FEATURE_TELNET_TTYPE
- ttype = getenv("TERM");
+ INIT_G();
+
+#if ENABLE_FEATURE_AUTOWIDTH
+ get_terminal_width_height(0, &G.win_width, &G.win_height);
+#endif
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+ G.ttype = getenv("TERM");
#endif
- memset(&G, 0, sizeof G);
+ if (tcgetattr(0, &G.termios_def) >= 0) {
+ G.do_termios = 1;
+ G.termios_raw = G.termios_def;
+ cfmakeraw(&G.termios_raw);
+ }
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ if (1 & getopt32(argv, "al:", &G.autologin))
+ G.autologin = getenv("USER");
+ argv += optind;
+#else
+ argv++;
+#endif
+ if (!*argv)
+ bb_show_usage();
+ host = *argv++;
+ port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
+ if (*argv) /* extra params?? */
+ bb_show_usage();
- if (tcgetattr(0, &G.termios_def) < 0)
- exit(1);
-
- G.termios_raw = G.termios_def;
- cfmakeraw(&G.termios_raw);
-
- if (argc < 2) bb_show_usage();
- port = (argc > 2)? argv[2] : "23";
-
- host = argv[1];
-
- G.netfd = xconnect(host, port);
+ xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
- setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
+ setsockopt(netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
- signal(SIGINT, fgotsig);
+ signal(SIGINT, record_signo);
#ifdef USE_POLL
- ufds[0].fd = 0; ufds[1].fd = G.netfd;
+ ufds[0].fd = 0; ufds[1].fd = netfd;
ufds[0].events = ufds[1].events = POLLIN;
-#else
+#else
FD_ZERO(&readfds);
- FD_SET(0, &readfds);
- FD_SET(G.netfd, &readfds);
- maxfd = G.netfd + 1;
+ FD_SET(STDIN_FILENO, &readfds);
+ FD_SET(netfd, &readfds);
+ maxfd = netfd + 1;
#endif
-
- while (1)
- {
+
+ while (1) {
#ifndef USE_POLL
fd_set rfds = readfds;
-
+
switch (select(maxfd, &rfds, NULL, NULL, NULL))
#else
switch (poll(ufds, 2, -1))
-#endif
+#endif
{
case 0:
/* timeout */
case -1:
/* error, ignore and/or log something, bay go to loop */
- if (G.gotsig)
- conescape();
+ if (bb_got_signal)
+ con_escape();
else
sleep(1);
break;
@@ -648,45 +620,31 @@ extern int telnet_main(int argc, char** argv)
#ifdef USE_POLL
if (ufds[0].revents) /* well, should check POLLIN, but ... */
-#else
- if (FD_ISSET(0, &rfds))
-#endif
+#else
+ if (FD_ISSET(STDIN_FILENO, &rfds))
+#endif
{
- len = read(0, G.buf, DATABUFSIZE);
-
+ len = read(STDIN_FILENO, G.buf, DATABUFSIZE);
if (len <= 0)
- doexit(0);
-
+ doexit(EXIT_SUCCESS);
TRACE(0, ("Read con: %d\n", len));
-
- handlenetoutput(len);
+ handle_net_output(len);
}
#ifdef USE_POLL
if (ufds[1].revents) /* well, should check POLLIN, but ... */
-#else
- if (FD_ISSET(G.netfd, &rfds))
-#endif
+#else
+ if (FD_ISSET(netfd, &rfds))
+#endif
{
- len = read(G.netfd, G.buf, DATABUFSIZE);
-
- if (len <= 0)
- {
- WriteCS(1, "Connection closed by foreign host.\r\n");
- doexit(1);
+ len = read(netfd, G.buf, DATABUFSIZE);
+ if (len <= 0) {
+ write_str(1, "Connection closed by foreign host\r\n");
+ doexit(EXIT_FAILURE);
}
- TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
-
- handlenetinput(len);
+ TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
+ handle_net_input(len);
}
}
- }
+ } /* while (1) */
}
-
-/*
-Local Variables:
-c-file-style: "linux"
-c-basic-offset: 4
-tab-width: 4
-End:
-*/
diff --git a/release/src/router/busybox/networking/telnetd.c b/release/src/router/busybox/networking/telnetd.c
index e41cfe58..4c5ea3ab 100644
--- a/release/src/router/busybox/networking/telnetd.c
+++ b/release/src/router/busybox/networking/telnetd.c
@@ -1,10 +1,9 @@
-/* $Id: telnetd.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
- *
+/* vi: set sw=4 ts=4: */
+/*
* Simple telnet server
* Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
*
- * This file is distributed under the Gnu Public License (GPL),
- * please see the file LICENSE for further information.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* ---------------------------------------------------------------------------
* (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
@@ -22,607 +21,660 @@
* Set process group corrections, initial busybox port
*/
-/*#define DEBUG 1 */
-
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <signal.h>
-#include <termios.h>
-#ifdef DEBUG
+#define DEBUG 0
+
+#include "libbb.h"
+#include <syslog.h>
+
+#if DEBUG
#define TELCMDS
#define TELOPTS
#endif
#include <arpa/telnet.h>
-#include <ctype.h>
-#include <sys/syslog.h>
-
-#include "busybox.h"
-
-#define BUFSIZE 4000
-
-static const char *loginpath =
-#ifdef CONFIG_LOGIN
-"/bin/login";
-#else
-"/bin/sh";
-#endif
-static const char *issuefile = "/etc/issue.net";
-
-/* shell name and arguments */
-
-static const char *argv_init[] = {NULL, NULL};
-
-/* structure that describes a session */
+/* Structure that describes a session */
struct tsession {
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- int sockfd_read, sockfd_write, ptyfd;
-#else /* CONFIG_FEATURE_TELNETD_INETD */
struct tsession *next;
- int sockfd, ptyfd;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- int shell_pid;
+ pid_t shell_pid;
+ int sockfd_read, sockfd_write, ptyfd;
+
/* two circular buffers */
- char *buf1, *buf2;
+ /*char *buf1, *buf2;*/
+/*#define TS_BUF1 ts->buf1*/
+/*#define TS_BUF2 TS_BUF2*/
+#define TS_BUF1 ((unsigned char*)(ts + 1))
+#define TS_BUF2 (((unsigned char*)(ts + 1)) + BUFSIZE)
int rdidx1, wridx1, size1;
int rdidx2, wridx2, size2;
};
-/*
-
- This is how the buffers are used. The arrows indicate the movement
- of data.
-
- +-------+ wridx1++ +------+ rdidx1++ +----------+
- | | <-------------- | buf1 | <-------------- | |
- | | size1-- +------+ size1++ | |
- | pty | | socket |
- | | rdidx2++ +------+ wridx2++ | |
- | | --------------> | buf2 | --------------> | |
- +-------+ size2++ +------+ size2-- +----------+
-
- Each session has got two buffers.
+/* Two buffers are directly after tsession in malloced memory.
+ * Make whole thing fit in 4k */
+enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
-*/
+/* Globals */
static int maxfd;
-
static struct tsession *sessions;
+static const char *loginpath = "/bin/login";
+static const char *issuefile = "/etc/issue.net";
/*
-
- Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
- and must be removed so as to not be interpreted by the terminal). Make an
- uninterrupted string of characters fit for the terminal. Do this by packing
- all characters meant for the terminal sequentially towards the end of bf.
+ Remove all IAC's from buf1 (received IACs are ignored and must be removed
+ so as to not be interpreted by the terminal). Make an uninterrupted
+ string of characters fit for the terminal. Do this by packing
+ all characters meant for the terminal sequentially towards the end of buf.
Return a pointer to the beginning of the characters meant for the terminal.
and make *num_totty the number of characters that should be sent to
the terminal.
Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
- past (bf + len) then that IAC will be left unprocessed and *processed will be
- less than len.
+ past (bf + len) then that IAC will be left unprocessed and *processed
+ will be less than len.
- FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
- what is the escape character? We aren't handling that situation here.
+ CR-LF ->'s CR mapping is also done here, for convenience.
- */
-static char *
-remove_iacs(struct tsession *ts, int *pnum_totty) {
- unsigned char *ptr0 = ts->buf1 + ts->wridx1;
+ NB: may fail to remove iacs which wrap around buffer!
+ */
+static unsigned char *
+remove_iacs(struct tsession *ts, int *pnum_totty)
+{
+ unsigned char *ptr0 = TS_BUF1 + ts->wridx1;
unsigned char *ptr = ptr0;
unsigned char *totty = ptr;
unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
- int processed;
int num_totty;
while (ptr < end) {
if (*ptr != IAC) {
- *totty++ = *ptr++;
+ char c = *ptr;
+
+ *totty++ = c;
+ ptr++;
+ /* We map \r\n ==> \r for pragmatic reasons.
+ * Many client implementations send \r\n when
+ * the user hits the CarriageReturn key.
+ */
+ if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
+ ptr++;
+ continue;
}
- else {
- if ((ptr+2) < end) {
- /* the entire IAC is contained in the buffer
- we were asked to process. */
-#ifdef DEBUG
- fprintf(stderr, "Ignoring IAC %s,%s\n",
- *ptr, TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
-#endif
- ptr += 3;
- } else {
- /* only the beginning of the IAC is in the
- buffer we were asked to process, we can't
- process this char. */
- break;
- }
+
+ if ((ptr+1) >= end)
+ break;
+ if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
+ ptr += 2;
+ continue;
+ }
+ if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
+ *totty++ = ptr[1];
+ ptr += 2;
+ continue;
+ }
+
+ /*
+ * TELOPT_NAWS support!
+ */
+ if ((ptr+2) >= end) {
+ /* only the beginning of the IAC is in the
+ buffer we were asked to process, we can't
+ process this char. */
+ break;
+ }
+ /*
+ * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
+ */
+ if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
+ struct winsize ws;
+ if ((ptr+8) >= end)
+ break; /* incomplete, can't process */
+ ws.ws_col = (ptr[3] << 8) | ptr[4];
+ ws.ws_row = (ptr[5] << 8) | ptr[6];
+ ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
+ ptr += 9;
+ continue;
}
+ /* skip 3-byte IAC non-SB cmd */
+#if DEBUG
+ fprintf(stderr, "Ignoring IAC %s,%s\n",
+ TELCMD(ptr[1]), TELOPT(ptr[2]));
+#endif
+ ptr += 3;
}
- processed = ptr - ptr0;
num_totty = totty - ptr0;
- /* the difference between processed and num_to tty
- is all the iacs we removed from the stream.
- Adjust buf1 accordingly. */
- ts->wridx1 += processed - num_totty;
- ts->size1 -= processed - num_totty;
*pnum_totty = num_totty;
- /* move the chars meant for the terminal towards the end of the
- buffer. */
+ /* the difference between ptr and totty is number of iacs
+ we removed from the stream. Adjust buf1 accordingly. */
+ if ((ptr - totty) == 0) /* 99.999% of cases */
+ return ptr0;
+ ts->wridx1 += ptr - totty;
+ ts->size1 -= ptr - totty;
+ /* move chars meant for the terminal towards the end of the buffer */
return memmove(ptr - num_totty, ptr0, num_totty);
}
-
-static int
-getpty(char *line)
+/*
+ * Converting single IAC into double on output
+ */
+static size_t iac_safe_write(int fd, const char *buf, size_t count)
{
- int p;
-#ifdef CONFIG_FEATURE_DEVPTS
- p = open("/dev/ptmx", 2);
- if (p > 0) {
- grantpt(p);
- unlockpt(p);
- strcpy(line, ptsname(p));
- return(p);
- }
-#else
- struct stat stb;
- int i;
- int j;
-
- strcpy(line, "/dev/ptyXX");
-
- for (i = 0; i < 16; i++) {
- line[8] = "pqrstuvwxyzabcde"[i];
- line[9] = '0';
- if (stat(line, &stb) < 0) {
+ const char *IACptr;
+ size_t wr, rc, total;
+
+ total = 0;
+ while (1) {
+ if (count == 0)
+ return total;
+ if (*buf == (char)IAC) {
+ static const char IACIAC[] ALIGN1 = { IAC, IAC };
+ rc = safe_write(fd, IACIAC, 2);
+ if (rc != 2)
+ break;
+ buf++;
+ total++;
+ count--;
continue;
}
- for (j = 0; j < 16; j++) {
- line[9] = j < 10 ? j + '0' : j - 10 + 'a';
- if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
- line[5] = 't';
- return p;
- }
- }
+ /* count != 0, *buf != IAC */
+ IACptr = memchr(buf, IAC, count);
+ wr = count;
+ if (IACptr)
+ wr = IACptr - buf;
+ rc = safe_write(fd, buf, wr);
+ if (rc != wr)
+ break;
+ buf += rc;
+ total += rc;
+ count -= rc;
}
-#endif /* CONFIG_FEATURE_DEVPTS */
- return -1;
-}
-
-
-static void
-send_iac(struct tsession *ts, unsigned char command, int option)
-{
- /* We rely on that there is space in the buffer for now. */
- char *b = ts->buf2 + ts->rdidx2;
- *b++ = IAC;
- *b++ = command;
- *b++ = option;
- ts->rdidx2 += 3;
- ts->size2 += 3;
+ /* here: rc - result of last short write */
+ if ((ssize_t)rc < 0) { /* error? */
+ if (total == 0)
+ return rc;
+ rc = 0;
+ }
+ return total + rc;
}
+/* Must match getopt32 string */
+enum {
+ OPT_WATCHCHILD = (1 << 2), /* -K */
+ OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
+ OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
+ OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
+};
static struct tsession *
-#ifdef CONFIG_FEATURE_TELNETD_INETD
-make_new_session(void)
-#else /* CONFIG_FEATURE_TELNETD_INETD */
-make_new_session(int sockfd)
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-{
+make_new_session(
+ USE_FEATURE_TELNETD_STANDALONE(int master_fd, int sock)
+ SKIP_FEATURE_TELNETD_STANDALONE(void)
+) {
+ const char *login_argv[2];
struct termios termbuf;
- int pty, pid;
- char tty_name[32];
- struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
-
- ts->buf1 = (char *)(&ts[1]);
- ts->buf2 = ts->buf1 + BUFSIZE;
-
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- ts->sockfd_read = 0;
- ts->sockfd_write = 1;
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- ts->sockfd = sockfd;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-
- ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
- ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
-
- /* Got a new connection, set up a tty and spawn a shell. */
-
- pty = getpty(tty_name);
-
- if (pty < 0) {
- syslog_msg(LOG_USER, LOG_ERR, "All network ports in use!");
- return 0;
+ int fd, pid;
+ char tty_name[GETPTY_BUFSIZE];
+ struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+
+ /*ts->buf1 = (char *)(ts + 1);*/
+ /*ts->buf2 = ts->buf1 + BUFSIZE;*/
+
+ /* Got a new connection, set up a tty. */
+ fd = xgetpty(tty_name);
+ if (fd > maxfd)
+ maxfd = fd;
+ ts->ptyfd = fd;
+ ndelay_on(fd);
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ ts->sockfd_read = sock;
+ /* SO_KEEPALIVE by popular demand */
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ ndelay_on(sock);
+ if (!sock) { /* We are called with fd 0 - we are in inetd mode */
+ sock++; /* so use fd 1 for output */
+ ndelay_on(sock);
}
-
- if (pty > maxfd)
- maxfd = pty;
-
- ts->ptyfd = pty;
-
+ ts->sockfd_write = sock;
+ if (sock > maxfd)
+ maxfd = sock;
+#else
+ /* SO_KEEPALIVE by popular demand */
+ setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ /* ts->sockfd_read = 0; - done by xzalloc */
+ ts->sockfd_write = 1;
+ ndelay_on(0);
+ ndelay_on(1);
+#endif
/* Make the telnet client understand we will echo characters so it
* should not do it locally. We don't tell the client to run linemode,
* because we want to handle line editing and tab completion and other
- * stuff that requires char-by-char support.
- */
-
- send_iac(ts, DO, TELOPT_ECHO);
- send_iac(ts, DO, TELOPT_LFLOW);
- send_iac(ts, WILL, TELOPT_ECHO);
- send_iac(ts, WILL, TELOPT_SGA);
-
-
- if ((pid = fork()) < 0) {
- syslog_msg(LOG_USER, LOG_ERR, "Can`t forking");
+ * stuff that requires char-by-char support. */
+ {
+ static const char iacs_to_send[] ALIGN1 = {
+ IAC, DO, TELOPT_ECHO,
+ IAC, DO, TELOPT_NAWS,
+ /* This requires telnetd.ctrlSQ.patch (incomplete) */
+ /* IAC, DO, TELOPT_LFLOW, */
+ IAC, WILL, TELOPT_ECHO,
+ IAC, WILL, TELOPT_SGA
+ };
+ /* This confuses iac_safe_write(), it will try to duplicate
+ * each IAC... */
+ //memcpy(TS_BUF2, iacs_to_send, sizeof(iacs_to_send));
+ //ts->rdidx2 = sizeof(iacs_to_send);
+ //ts->size2 = sizeof(iacs_to_send);
+ /* So just stuff it into TCP stream! (no error check...) */
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
+#else
+ safe_write(1, iacs_to_send, sizeof(iacs_to_send));
+#endif
+ /*ts->rdidx2 = 0; - xzalloc did it */
+ /*ts->size2 = 0;*/
}
- if (pid == 0) {
- /* In child, open the child's side of the tty. */
- int i;
-
- for(i = 0; i <= maxfd; i++)
- close(i);
- /* make new process group */
- setsid();
-
- if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
- syslog_msg(LOG_USER, LOG_ERR, "Could not open tty");
- exit(1);
- }
- dup(0);
- dup(0);
-
- tcsetpgrp(0, getpid());
-
- /* The pseudo-terminal allocated to the client is configured to operate in
- * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
- */
-
- tcgetattr(0, &termbuf);
- termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
- termbuf.c_oflag |= ONLCR|XTABS;
- termbuf.c_iflag |= ICRNL;
- termbuf.c_iflag &= ~IXOFF;
- /*termbuf.c_lflag &= ~ICANON;*/
- tcsetattr(0, TCSANOW, &termbuf);
- print_login_issue(issuefile, NULL);
-
- /* exec shell, with correct argv and env */
- execv(loginpath, (char *const *)argv_init);
+ fflush(NULL); /* flush all streams */
+ pid = vfork(); /* NOMMU-friendly */
+ if (pid < 0) {
+ free(ts);
+ close(fd);
+ /* sock will be closed by caller */
+ bb_perror_msg("vfork");
+ return NULL;
+ }
+ if (pid > 0) {
+ /* Parent */
+ ts->shell_pid = pid;
+ return ts;
+ }
- /* NOT REACHED */
- syslog_msg(LOG_USER, LOG_ERR, "execv error");
- exit(1);
+ /* Child */
+ /* Careful - we are after vfork! */
+
+ /* Restore default signal handling ASAP */
+ bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ if (!(option_mask32 & OPT_INETD)) {
+ struct tsession *tp = sessions;
+ while (tp) {
+ close(tp->ptyfd);
+ close(tp->sockfd_read);
+ /* sockfd_write == sockfd_read for standalone telnetd */
+ /*close(tp->sockfd_write);*/
+ tp = tp->next;
+ }
}
+#endif
+
+ /* Make new session and process group */
+ setsid();
- ts->shell_pid = pid;
+ close(fd);
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ close(sock);
+ if (master_fd >= 0)
+ close(master_fd);
+#endif
- return ts;
+ /* Open the child's side of the tty. */
+ /* NB: setsid() disconnects from any previous ctty's. Therefore
+ * we must open child's side of the tty AFTER setsid! */
+ close(0);
+ xopen(tty_name, O_RDWR); /* becomes our ctty */
+ xdup2(0, 1);
+ xdup2(0, 2);
+ tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
+
+ /* The pseudo-terminal allocated to the client is configured to operate in
+ * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
+ tcgetattr(0, &termbuf);
+ termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
+ termbuf.c_oflag |= ONLCR | XTABS;
+ termbuf.c_iflag |= ICRNL;
+ termbuf.c_iflag &= ~IXOFF;
+ /*termbuf.c_lflag &= ~ICANON;*/
+ tcsetattr_stdin_TCSANOW(&termbuf);
+
+ /* Uses FILE-based I/O to stdout, but does fflush(stdout),
+ * so should be safe with vfork.
+ * I fear, though, that some users will have ridiculously big
+ * issue files, and they may block writing to fd 1,
+ * (parent is supposed to read it, but parent waits
+ * for vforked child to exec!) */
+ print_login_issue(issuefile, tty_name);
+
+ /* Exec shell / login / whatever */
+ login_argv[0] = loginpath;
+ login_argv[1] = NULL;
+ /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
+ * exec external program */
+ BB_EXECVP(loginpath, (char **)login_argv);
+ /* _exit is safer with vfork, and we shouldn't send message
+ * to remote clients anyway */
+ _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", loginpath);*/
}
-#ifndef CONFIG_FEATURE_TELNETD_INETD
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+
static void
free_session(struct tsession *ts)
{
struct tsession *t = sessions;
- /* Unlink this telnet session from the session list. */
- if(t == ts)
+ if (option_mask32 & OPT_INETD)
+ exit(EXIT_SUCCESS);
+
+ /* Unlink this telnet session from the session list */
+ if (t == ts)
sessions = ts->next;
else {
- while(t->next != ts)
+ while (t->next != ts)
t = t->next;
t->next = ts->next;
}
+#if 0
+ /* It was said that "normal" telnetd just closes ptyfd,
+ * doesn't send SIGKILL. When we close ptyfd,
+ * kernel sends SIGHUP to processes having slave side opened. */
kill(ts->shell_pid, SIGKILL);
-
- wait4(ts->shell_pid, NULL, 0, NULL);
-
+ waitpid(ts->shell_pid, NULL, 0);
+#endif
close(ts->ptyfd);
- close(ts->sockfd);
-
- if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
- maxfd--;
- if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
- maxfd--;
-
+ close(ts->sockfd_read);
+ /* We do not need to close(ts->sockfd_write), it's the same
+ * as sockfd_read unless we are in inetd mode. But in inetd mode
+ * we do not reach this */
free(ts);
-}
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-int
-telnetd_main(int argc, char **argv)
-{
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- struct sockaddr_in sa;
- int master_fd;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- fd_set rdfdset, wrfdset;
- int selret;
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- int on = 1;
- int portnbr = 23;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- int c;
- static const char options[] =
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- "f:l:";
-#else /* CONFIG_EATURE_TELNETD_INETD */
- "f:l:p:";
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- int maxlen, w, r;
-
- for (;;) {
- c = getopt( argc, argv, options);
- if (c == EOF) break;
- switch (c) {
- case 'f':
- issuefile = strdup (optarg);
- break;
- case 'l':
- loginpath = strdup (optarg);
- break;
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- case 'p':
- portnbr = atoi(optarg);
- break;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- default:
- bb_show_usage();
- }
+ /* Scan all sessions and find new maxfd */
+ maxfd = 0;
+ ts = sessions;
+ while (ts) {
+ if (maxfd < ts->ptyfd)
+ maxfd = ts->ptyfd;
+ if (maxfd < ts->sockfd_read)
+ maxfd = ts->sockfd_read;
+#if 0
+ /* Again, sockfd_write == sockfd_read here */
+ if (maxfd < ts->sockfd_write)
+ maxfd = ts->sockfd_write;
+#endif
+ ts = ts->next;
}
+}
- if (access(loginpath, X_OK) < 0) {
- bb_error_msg_and_die ("'%s' unavailable.", loginpath);
- }
+#else /* !FEATURE_TELNETD_STANDALONE */
- argv_init[0] = loginpath;
+/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
+#define free_session(ts) return 0
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- maxfd = 1;
- sessions = make_new_session();
-#else /* CONFIG_EATURE_TELNETD_INETD */
- sessions = 0;
+#endif
- /* Grab a TCP socket. */
+static void handle_sigchld(int sig UNUSED_PARAM)
+{
+ pid_t pid;
+ struct tsession *ts;
- master_fd = socket(AF_INET, SOCK_STREAM, 0);
- if (master_fd < 0) {
- bb_perror_msg_and_die("socket");
+ /* Looping: more than one child may have exited */
+ while (1) {
+ pid = wait_any_nohang(NULL);
+ if (pid <= 0)
+ break;
+ ts = sessions;
+ while (ts) {
+ if (ts->shell_pid == pid) {
+ ts->shell_pid = -1;
+ break;
+ }
+ ts = ts->next;
+ }
}
- (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- /* Set it to listen to specified port. */
-
- memset((void *)&sa, 0, sizeof(sa));
- sa.sin_family = AF_INET;
- sa.sin_port = htons(portnbr);
+}
- if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
- bb_perror_msg_and_die("bind");
+int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int telnetd_main(int argc UNUSED_PARAM, char **argv)
+{
+ fd_set rdfdset, wrfdset;
+ unsigned opt;
+ int count;
+ struct tsession *ts;
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+#define IS_INETD (opt & OPT_INETD)
+ int master_fd = master_fd; /* be happy, gcc */
+ unsigned portnbr = 23;
+ char *opt_bindaddr = NULL;
+ char *opt_portnbr;
+#else
+ enum {
+ IS_INETD = 1,
+ master_fd = -1,
+ portnbr = 23,
+ };
+#endif
+ /* Even if !STANDALONE, we accept (and ignore) -i, thus people
+ * don't need to guess whether it's ok to pass -i to us */
+ opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
+ &issuefile, &loginpath
+ USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
+ if (!IS_INETD /*&& !re_execed*/) {
+ /* inform that we start in standalone mode?
+ * May be useful when people forget to give -i */
+ /*bb_error_msg("listening for connections");*/
+ if (!(opt & OPT_FOREGROUND)) {
+ /* DAEMON_CHDIR_ROOT was giving inconsistent
+ * behavior with/without -F, -i */
+ bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
+ }
}
-
- if (listen(master_fd, 1) < 0) {
- bb_perror_msg_and_die("listen");
+ /* Redirect log to syslog early, if needed */
+ if (IS_INETD || !(opt & OPT_FOREGROUND)) {
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
}
+ USE_FEATURE_TELNETD_STANDALONE(
+ if (opt & OPT_PORT)
+ portnbr = xatou16(opt_portnbr);
+ );
+
+ /* Used to check access(loginpath, X_OK) here. Pointless.
+ * exec will do this for us for free later. */
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ if (IS_INETD) {
+ sessions = make_new_session(-1, 0);
+ if (!sessions) /* pty opening or vfork problem, exit */
+ return 1; /* make_new_session prints error message */
+ } else {
+ master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
+ xlisten(master_fd, 1);
+ }
+#else
+ sessions = make_new_session();
+ if (!sessions) /* pty opening or vfork problem, exit */
+ return 1; /* make_new_session prints error message */
+#endif
- if (daemon(0, 0) < 0)
- bb_perror_msg_and_die("daemon");
-
-
- maxfd = master_fd;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ /* We don't want to die if just one session is broken */
+ signal(SIGPIPE, SIG_IGN);
- do {
- struct tsession *ts;
+ if (opt & OPT_WATCHCHILD)
+ signal(SIGCHLD, handle_sigchld);
+ else /* prevent dead children from becoming zombies */
+ signal(SIGCHLD, SIG_IGN);
- FD_ZERO(&rdfdset);
- FD_ZERO(&wrfdset);
+/*
+ This is how the buffers are used. The arrows indicate the movement
+ of data.
+ +-------+ wridx1++ +------+ rdidx1++ +----------+
+ | | <-------------- | buf1 | <-------------- | |
+ | | size1-- +------+ size1++ | |
+ | pty | | socket |
+ | | rdidx2++ +------+ wridx2++ | |
+ | | --------------> | buf2 | --------------> | |
+ +-------+ size2++ +------+ size2-- +----------+
- /* select on the master socket, all telnet sockets and their
- * ptys if there is room in their respective session buffers.
- */
+ size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
+ size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
-#ifndef CONFIG_FEATURE_TELNETD_INETD
+ Each session has got two buffers. Buffers are circular. If sizeN == 0,
+ buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
+ rdidxN == wridxN.
+*/
+ again:
+ FD_ZERO(&rdfdset);
+ FD_ZERO(&wrfdset);
+
+ /* Select on the master socket, all telnet sockets and their
+ * ptys if there is room in their session buffers.
+ * NB: scalability problem: we recalculate entire bitmap
+ * before each select. Can be a problem with 500+ connections. */
+ ts = sessions;
+ while (ts) {
+ struct tsession *next = ts->next; /* in case we free ts. */
+ if (ts->shell_pid == -1) {
+ /* Child died and we detected that */
+ free_session(ts);
+ } else {
+ if (ts->size1 > 0) /* can write to pty */
+ FD_SET(ts->ptyfd, &wrfdset);
+ if (ts->size1 < BUFSIZE) /* can read from socket */
+ FD_SET(ts->sockfd_read, &rdfdset);
+ if (ts->size2 > 0) /* can write to socket */
+ FD_SET(ts->sockfd_write, &wrfdset);
+ if (ts->size2 < BUFSIZE) /* can read from pty */
+ FD_SET(ts->ptyfd, &rdfdset);
+ }
+ ts = next;
+ }
+ if (!IS_INETD) {
FD_SET(master_fd, &rdfdset);
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ /* This is needed because free_session() does not
+ * take master_fd into account when it finds new
+ * maxfd among remaining fd's */
+ if (master_fd > maxfd)
+ maxfd = master_fd;
+ }
- ts = sessions;
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- while (ts) {
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- /* buf1 is used from socket to pty
- * buf2 is used from pty to socket
- */
- if (ts->size1 > 0) {
- FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
- }
- if (ts->size1 < BUFSIZE) {
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- }
- if (ts->size2 > 0) {
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- }
- if (ts->size2 < BUFSIZE) {
- FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
- }
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- ts = ts->next;
+ count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
+ if (count < 0)
+ goto again; /* EINTR or ENOMEM */
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ /* First check for and accept new sessions. */
+ if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
+ int fd;
+ struct tsession *new_ts;
+
+ fd = accept(master_fd, NULL, NULL);
+ if (fd < 0)
+ goto again;
+ /* Create a new session and link it into our active list */
+ new_ts = make_new_session(master_fd, fd);
+ if (new_ts) {
+ new_ts->next = sessions;
+ sessions = new_ts;
+ } else {
+ close(fd);
}
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-
- selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
-
- if (!selret)
- break;
+ }
+#endif
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- /* First check for and accept new sessions. */
- if (FD_ISSET(master_fd, &rdfdset)) {
- int fd, salen;
-
- salen = sizeof(sa);
- if ((fd = accept(master_fd, (struct sockaddr *)&sa,
- &salen)) < 0) {
- continue;
- } else {
- /* Create a new session and link it into
- our active list. */
- struct tsession *new_ts = make_new_session(fd);
- if (new_ts) {
- new_ts->next = sessions;
- sessions = new_ts;
- if (fd > maxfd)
- maxfd = fd;
- } else {
- close(fd);
- }
+ /* Then check for data tunneling. */
+ ts = sessions;
+ while (ts) { /* For all sessions... */
+ struct tsession *next = ts->next; /* in case we free ts. */
+
+ if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
+ int num_totty;
+ unsigned char *ptr;
+ /* Write to pty from buffer 1. */
+ ptr = remove_iacs(ts, &num_totty);
+ count = safe_write(ts->ptyfd, ptr, num_totty);
+ if (count < 0) {
+ if (errno == EAGAIN)
+ goto skip1;
+ goto kill_session;
}
+ ts->size1 -= count;
+ ts->wridx1 += count;
+ if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
+ ts->wridx1 = 0;
}
-
- /* Then check for data tunneling. */
-
- ts = sessions;
- while (ts) { /* For all sessions... */
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- struct tsession *next = ts->next; /* in case we free ts. */
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-
- if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
- int num_totty;
- char *ptr;
- /* Write to pty from buffer 1. */
-
- ptr = remove_iacs(ts, &num_totty);
-
- w = write(ts->ptyfd, ptr, num_totty);
- if (w < 0) {
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- exit(0);
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- free_session(ts);
- ts = next;
- continue;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- }
- ts->wridx1 += w;
- ts->size1 -= w;
- if (ts->wridx1 == BUFSIZE)
- ts->wridx1 = 0;
- }
-
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- /* Write to socket from buffer 2. */
- maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
- if (w < 0)
- exit(0);
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
- if (w < 0) {
- free_session(ts);
- ts = next;
- continue;
- }
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- ts->wridx2 += w;
- ts->size2 -= w;
- if (ts->wridx2 == BUFSIZE)
- ts->wridx2 = 0;
+ skip1:
+ if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
+ /* Write to socket from buffer 2. */
+ count = MIN(BUFSIZE - ts->wridx2, ts->size2);
+ count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2 + ts->wridx2), count);
+ if (count < 0) {
+ if (errno == EAGAIN)
+ goto skip2;
+ goto kill_session;
}
+ ts->size2 -= count;
+ ts->wridx2 += count;
+ if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
+ ts->wridx2 = 0;
+ }
+ skip2:
+ /* Should not be needed, but... remove_iacs is actually buggy
+ * (it cannot process iacs which wrap around buffer's end)!
+ * Since properly fixing it requires writing bigger code,
+ * we rely instead on this code making it virtually impossible
+ * to have wrapped iac (people don't type at 2k/second).
+ * It also allows for bigger reads in common case. */
+ if (ts->size1 == 0) {
+ ts->rdidx1 = 0;
+ ts->wridx1 = 0;
+ }
+ if (ts->size2 == 0) {
+ ts->rdidx2 = 0;
+ ts->wridx2 = 0;
+ }
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- /* Read from socket to buffer 1. */
- maxlen = MIN(BUFSIZE - ts->rdidx1,
- BUFSIZE - ts->size1);
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
- if (!r || (r < 0 && errno != EINTR))
- exit(0);
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
- if (!r || (r < 0 && errno != EINTR)) {
- free_session(ts);
- ts = next;
- continue;
- }
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
- r--;
- if(!r)
- continue;
- }
- ts->rdidx1 += r;
- ts->size1 += r;
- if (ts->rdidx1 == BUFSIZE)
- ts->rdidx1 = 0;
+ if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
+ /* Read from socket to buffer 1. */
+ count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
+ count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
+ if (count <= 0) {
+ if (count < 0 && errno == EAGAIN)
+ goto skip3;
+ goto kill_session;
}
-
- if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
- /* Read from pty to buffer 2. */
- maxlen = MIN(BUFSIZE - ts->rdidx2,
- BUFSIZE - ts->size2);
- r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
- if (!r || (r < 0 && errno != EINTR)) {
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- exit(0);
-#else /* CONFIG_FEATURE_TELNETD_INETD */
- free_session(ts);
- ts = next;
- continue;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- }
- ts->rdidx2 += r;
- ts->size2 += r;
- if (ts->rdidx2 == BUFSIZE)
- ts->rdidx2 = 0;
+ /* Ignore trailing NUL if it is there */
+ if (!TS_BUF1[ts->rdidx1 + count - 1]) {
+ --count;
}
-
- if (ts->size1 == 0) {
+ ts->size1 += count;
+ ts->rdidx1 += count;
+ if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
ts->rdidx1 = 0;
- ts->wridx1 = 0;
+ }
+ skip3:
+ if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
+ /* Read from pty to buffer 2. */
+ count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
+ count = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, count);
+ if (count <= 0) {
+ if (count < 0 && errno == EAGAIN)
+ goto skip4;
+ goto kill_session;
}
- if (ts->size2 == 0) {
+ ts->size2 += count;
+ ts->rdidx2 += count;
+ if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
ts->rdidx2 = 0;
- ts->wridx2 = 0;
- }
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- ts = next;
}
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
-
- } while (1);
+ skip4:
+ ts = next;
+ continue;
+ kill_session:
+ free_session(ts);
+ ts = next;
+ }
- return 0;
+ goto again;
}
diff --git a/release/src/router/busybox/networking/telnetd.ctrlSQ.patch b/release/src/router/busybox/networking/telnetd.ctrlSQ.patch
new file mode 100644
index 00000000..885e1052
--- /dev/null
+++ b/release/src/router/busybox/networking/telnetd.ctrlSQ.patch
@@ -0,0 +1,175 @@
+From: "Doug Graham" <dgraham@nortel.com>
+Date: 2009-01-22 07:20
+
+Hello,
+
+Busybox's telnetd does not disable local (client-side) flow control
+properly. It does not put the pty into packet mode and then notify the
+client whenever flow control is disabled by an application running under
+its control. The result is that ^S/^Q are not passed through to the
+application, which is painful when the application is an emacs variant.
+
+I suppose that support for this might be considered bloat, but the
+included patch only adds about 200 bytes of text to x86 busybox and 300
+bytes to mipsel busybox. Please consider applying.
+
+=============================
+
+NB: the patch doesn't work as-is because we now have iac_safe_write()
+which quotes IACs on output.
+
+=============================
+Docs:
+
+The following ioctl(2) calls apply only to pseudo terminals:
+
+TIOCSTOP Stops output to a terminal (e.g. like typing ^S). Takes no parameter.
+
+TIOCSTART Restarts output (stopped by TIOCSTOP or by typing ^S). Takes no parameter.
+
+TIOCPKT Enable/disable packet mode. When applied to the master side of a pseudo terminal, each
+subsequent read(2) from the terminal will return data written on the slave part of the pseudo terminal preceded by a
+zero byte (symbolically defined as TIOCPKT_DATA), or a single byte reflecting control status information.
+In the latter case, the byte is an inclusive-or of zero or more of the bits:
+
+TIOCPKT_FLUSHREAD whenever the read queue for the terminal is flushed.
+TIOCPKT_FLUSHWRITE whenever the write queue for the terminal is flushed.
+TIOCPKT_STOP whenever output to the terminal is stopped a la ^S.
+TIOCPKT_START whenever output to the terminal is restarted.
+TIOCPKT_DOSTOP whenever t_stopc is ^S and t_startc is ^Q.
+TIOCPKT_NOSTOP whenever the start and stop characters are not ^S/^Q.
+
+While this mode is in use, the presence of control status information to be read from the master side may be detected
+by a select(2) for exceptional conditions.
+
+This mode is used by rlogin(1) and rlogind(8) to implement a remote-echoed, locally ^S/^Q flow-controlled remote login
+with proper back-flushing of output; it can be used by other similar programs.
+
+TIOCUCNTL Enable/disable a mode that allows a small number of simple user ioctl(2) commands to be passed through
+the pseudo-terminal, using a protocol similar to that of TIOCPKT. The TIOCUCNTL and TIOCPKT modes are mutually
+exclusive. This mode is enabled from the master side of a pseudo terminal. Each subsequent read(2) from the master side
+will return data written on the slave part of the pseudo terminal preceded by a zero byte, or a single byte reflecting a
+user control operation on the slave side. A user control command consists of a special ioctl(2) operation with no data;
+the command is given as UIOCCMD (n), where n is a number in the range 1-255. The operation value n will be received as
+a single byte on the next read(2) from the master side. The ioctl(2) UIOCCMD (0) is a no-op that may be used to probe
+for the existence of this facility. As with TIOCPKT mode, command operations may be detected with a select(2) for
+exceptional conditions.
+
+--- busybox-1.13.2/networking/telnetd.c 2009/01/21 20:02:39 1.1
++++ busybox-1.13.2/networking/telnetd.c 2009/01/22 00:35:28
+@@ -38,6 +38,9 @@
+ int sockfd_read, sockfd_write, ptyfd;
+ int shell_pid;
+
++#ifdef TIOCPKT
++ int flowstate;
++#endif
+ /* two circular buffers */
+ /*char *buf1, *buf2;*/
+ /*#define TS_BUF1 ts->buf1*/
+@@ -170,6 +173,9 @@
+ int fd, pid;
+ char tty_name[GETPTY_BUFSIZE];
+ struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
++#ifdef TIOCPKT
++ int on = 1;
++#endif
+
+ /*ts->buf1 = (char *)(ts + 1);*/
+ /*ts->buf2 = ts->buf1 + BUFSIZE;*/
+@@ -180,6 +186,10 @@
+ maxfd = fd;
+ ts->ptyfd = fd;
+ ndelay_on(fd);
++#ifdef TIOCPKT
++ ioctl(fd, TIOCPKT, &on);
++ ts->flowstate = TIOCPKT_DOSTOP;
++#endif
+ #if ENABLE_FEATURE_TELNETD_STANDALONE
+ ts->sockfd_read = sock;
+ /* SO_KEEPALIVE by popular demand */
+@@ -385,6 +395,16 @@
+ portnbr = 23,
+ };
+ #endif
++#ifdef TIOCPKT
++ int control;
++ static const char lflow_on[] =
++ {IAC, SB, TELOPT_LFLOW, LFLOW_ON, IAC, SE};
++ static const char lflow_off[] =
++ {IAC, SB, TELOPT_LFLOW, LFLOW_OFF, IAC, SE};
++# define RESERVED sizeof(lflow_on)
++#else
++# define RESERVED 0
++#endif
+ /* Even if !STANDALONE, we accept (and ignore) -i, thus people
+ * don't need to guess whether it's ok to pass -i to us */
+ opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
+@@ -475,7 +495,7 @@
+ FD_SET(ts->sockfd_read, &rdfdset);
+ if (ts->size2 > 0) /* can write to socket */
+ FD_SET(ts->sockfd_write, &wrfdset);
+- if (ts->size2 < BUFSIZE) /* can read from pty */
++ if (ts->size2 < (BUFSIZE - RESERVED)) /* can read from pty */
+ FD_SET(ts->ptyfd, &rdfdset);
+ }
+ ts = next;
+@@ -593,6 +613,52 @@
+ goto skip4;
+ goto kill_session;
+ }
++#ifdef TIOCPKT
++ control = TS_BUF2[ts->rdidx2];
++ if (--count > 0 && control == TIOCPKT_DATA) {
++ /*
++ * If we are in packet mode, and we have
++ * just read a chunk of actual data from
++ * the pty, then there is the TIOCPKT_DATA
++ * byte (zero) that we have got to remove
++ * somehow. If there were no chars in
++ * TS_BUF2 before we did this read, then
++ * we can optimize by just advancing wridx2.
++ * Otherwise we have to copy the new data down
++ * to close the gap (Could use readv() instead).
++ */
++ if (ts->size2 == 0)
++ ts->wridx2++;
++ else {
++ memmove(TS_BUF2 + ts->rdidx2,
++ TS_BUF2 + ts->rdidx2 + 1, count);
++ }
++ }
++
++ /*
++ * If the flow control state changed, notify
++ * the client. If "control" is not TIOCPKT_DATA,
++ * then there are no data bytes to worry about.
++ */
++ if ((control & (TIOCPKT_DOSTOP|TIOCPKT_NOSTOP)) != 0
++ && ts->flowstate != (control & TIOCPKT_DOSTOP)) {
++ const char *p = ts->flowstate ? lflow_off : lflow_on;
++
++ /*
++ * We know we have enough free slots available
++ * (see RESERVED) but they are not necessarily
++ * contiguous; we may have to wrap.
++ */
++ for (count = sizeof(lflow_on); count > 0; count--) {
++ TS_BUF2[ts->rdidx2++] = *p++;
++ if (ts->rdidx2 >= BUFSIZE)
++ ts->rdidx2 = 0;
++ ts->size2++;
++ }
++
++ ts->flowstate = control & TIOCPKT_DOSTOP;
++ }
++#endif /* TIOCPKT */
+ ts->size2 += count;
+ ts->rdidx2 += count;
+ if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
+
+--Doug
+_______________________________________________
+busybox mailing list
+busybox@busybox.net
+http://lists.busybox.net/mailman/listinfo/busybox
diff --git a/release/src/router/busybox/networking/tftp.c b/release/src/router/busybox/networking/tftp.c
index a1a79a09..9c78b6e1 100644
--- a/release/src/router/busybox/networking/tftp.c
+++ b/release/src/router/busybox/networking/tftp.c
@@ -1,58 +1,37 @@
-/* ------------------------------------------------------------------------- */
-/* tftp.c */
-/* */
-/* A simple tftp client for busybox. */
-/* Tries to follow RFC1350. */
-/* Only "octet" mode supported. */
-/* Optional blocksize negotiation (RFC2347 + RFC2348) */
-/* */
-/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
-/* */
-/* Parts of the code based on: */
-/* */
-/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
-/* and Remi Lefebvre <remi@debian.org> */
-/* */
-/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
-/* */
-/* This program is free software; you can redistribute it and/or modify */
-/* it under the terms of the GNU General Public License as published by */
-/* the Free Software Foundation; either version 2 of the License, or */
-/* (at your option) any later version. */
-/* */
-/* This program is distributed in the hope that it will be useful, */
-/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
-/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
-/* General Public License for more details. */
-/* */
-/* You should have received a copy of the GNU General Public License */
-/* along with this program; if not, write to the Free Software */
-/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* */
-/* ------------------------------------------------------------------------- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "busybox.h"
-
-//#define CONFIG_FEATURE_TFTP_DEBUG
-
-#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
-#define TFTP_TIMEOUT 5 /* seconds */
+/* vi: set sw=4 ts=4: */
+/* -------------------------------------------------------------------------
+ * tftp.c
+ *
+ * A simple tftp client/server for busybox.
+ * Tries to follow RFC1350.
+ * Only "octet" mode supported.
+ * Optional blocksize negotiation (RFC2347 + RFC2348)
+ *
+ * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
+ *
+ * Parts of the code based on:
+ *
+ * atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
+ * and Remi Lefebvre <remi@debian.org>
+ *
+ * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
+ *
+ * tftpd added by Denys Vlasenko & Vladimir Dronnikov
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * ------------------------------------------------------------------------- */
+
+#include "libbb.h"
+
+#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
+
+#define TFTP_BLKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_BLKSIZE_DEFAULT_STR "512"
+#define TFTP_TIMEOUT_MS 50
+#define TFTP_MAXTIMEOUT_MS 2000
+#define TFTP_NUM_RETRIES 12 /* number of backed-off retries */
/* opcodes we support */
-
#define TFTP_RRQ 1
#define TFTP_WRQ 2
#define TFTP_DATA 3
@@ -60,528 +39,702 @@
#define TFTP_ERROR 5
#define TFTP_OACK 6
-static const char *tftp_bb_error_msg[] = {
- "Undefined error",
- "File not found",
- "Access violation",
- "Disk full or allocation error",
- "Illegal TFTP operation",
- "Unknown transfer ID",
- "File already exists",
- "No such user"
+/* error codes sent over network (we use only 0, 1, 3 and 8) */
+/* generic (error message is included in the packet) */
+#define ERR_UNSPEC 0
+#define ERR_NOFILE 1
+#define ERR_ACCESS 2
+/* disk full or allocation exceeded */
+#define ERR_WRITE 3
+#define ERR_OP 4
+#define ERR_BAD_ID 5
+#define ERR_EXIST 6
+#define ERR_BAD_USER 7
+#define ERR_BAD_OPT 8
+
+/* masks coming from getopt32 */
+enum {
+ TFTP_OPT_GET = (1 << 0),
+ TFTP_OPT_PUT = (1 << 1),
+ /* pseudo option: if set, it's tftpd */
+ TFTPD_OPT = (1 << 7) * ENABLE_TFTPD,
+ TFTPD_OPT_r = (1 << 8) * ENABLE_TFTPD,
+ TFTPD_OPT_c = (1 << 9) * ENABLE_TFTPD,
+ TFTPD_OPT_u = (1 << 10) * ENABLE_TFTPD,
+};
+
+#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(...)
+#define CMD_GET(cmd) 1
+#define CMD_PUT(cmd) 0
+#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(...)
+#define CMD_GET(cmd) 0
+#define CMD_PUT(cmd) 1
+#else
+#define USE_GETPUT(...) __VA_ARGS__
+#define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
+#define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
+#endif
+/* NB: in the code below
+ * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
+ */
+
+
+struct globals {
+ /* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */
+ uint8_t error_pkt[4 + 32];
+ char *user_opt;
+ /* used in tftpd_main(), a bit big for stack: */
+ char block_buf[TFTP_BLKSIZE_DEFAULT];
};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define block_buf (G.block_buf )
+#define user_opt (G.user_opt )
+#define error_pkt (G.error_pkt )
+#define INIT_G() do { } while (0)
+
+#define error_pkt_reason (error_pkt[3])
+#define error_pkt_str (error_pkt + 4)
-const int tftp_cmd_get = 1;
-const int tftp_cmd_put = 2;
-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
-static int tftp_blocksize_check(int blocksize, int bufsize)
+static int tftp_blksize_check(const char *blksize_str, int maxsize)
{
- /* Check if the blocksize is valid:
+ /* Check if the blksize is valid:
* RFC2348 says between 8 and 65464,
* but our implementation makes it impossible
- * to use blocksizes smaller than 22 octets.
- */
-
- if ((bufsize && (blocksize > bufsize)) ||
- (blocksize < 8) || (blocksize > 65464)) {
- bb_error_msg("bad blocksize");
- return 0;
+ * to use blksizes smaller than 22 octets. */
+ unsigned blksize = bb_strtou(blksize_str, NULL, 10);
+ if (errno
+ || (blksize < 24) || (blksize > maxsize)
+ ) {
+ bb_error_msg("bad blocksize '%s'", blksize_str);
+ return -1;
}
-
- return blocksize;
+#if ENABLE_TFTP_DEBUG
+ bb_error_msg("using blksize %u", blksize);
+#endif
+ return blksize;
}
-static char *tftp_option_get(char *buf, int len, char *option)
+static char *tftp_get_option(const char *option, char *buf, int len)
{
- int opt_val = 0;
+ int opt_val = 0;
int opt_found = 0;
int k;
-
- while (len > 0) {
- /* Make sure the options are terminated correctly */
+ /* buf points to:
+ * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */
- for (k = 0; k < len; k++) {
- if (buf[k] == '\0') {
- break;
+ while (len > 0) {
+ /* Make sure options are terminated correctly */
+ for (k = 0; k < len; k++) {
+ if (buf[k] == '\0') {
+ goto nul_found;
}
}
-
- if (k >= len) {
- break;
- }
-
- if (opt_val == 0) {
+ return NULL;
+ nul_found:
+ if (opt_val == 0) { /* it's "name" part */
if (strcasecmp(buf, option) == 0) {
- opt_found = 1;
- }
- }
- else {
- if (opt_found) {
- return buf;
+ opt_found = 1;
}
+ } else if (opt_found) {
+ return buf;
}
-
+
k++;
-
buf += k;
len -= k;
-
opt_val ^= 1;
}
-
+
return NULL;
}
#endif
-static inline int tftp(const int cmd, const struct hostent *host,
- const char *remotefile, int localfd, const int port, int tftp_bufsize)
+static int tftp_protocol(
+ len_and_sockaddr *our_lsa,
+ len_and_sockaddr *peer_lsa,
+ const char *local_file
+ USE_TFTP(, const char *remote_file)
+ USE_FEATURE_TFTP_BLOCKSIZE(USE_TFTPD(, void *tsize))
+ USE_FEATURE_TFTP_BLOCKSIZE(, int blksize))
{
- const int cmd_get = cmd & tftp_cmd_get;
- const int cmd_put = cmd & tftp_cmd_put;
- const int bb_tftp_num_retries = 5;
-
- struct sockaddr_in sa;
- struct sockaddr_in from;
- struct timeval tv;
- socklen_t fromlen;
- fd_set rfds;
- char *cp;
- unsigned short tmp;
- int socketfd;
- int len;
- int opcode = 0;
- int finished = 0;
- int timeout = bb_tftp_num_retries;
- int block_nr = 1;
-
-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
- int want_option_ack = 0;
+#if !ENABLE_TFTP
+#define remote_file NULL
+#endif
+#if !(ENABLE_FEATURE_TFTP_BLOCKSIZE && ENABLE_TFTPD)
+#define tsize NULL
+#endif
+#if !ENABLE_FEATURE_TFTP_BLOCKSIZE
+ enum { blksize = TFTP_BLKSIZE_DEFAULT };
#endif
+ struct pollfd pfd[1];
+#define socket_fd (pfd[0].fd)
+ int len;
+ int send_len;
+ USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
+ smallint finished = 0;
+ uint16_t opcode;
+ uint16_t block_nr;
+ uint16_t recv_blk;
+ int open_mode, local_fd;
+ int retries, waittime_ms;
+ int io_bufsize = blksize + 4;
+ char *cp;
/* Can't use RESERVE_CONFIG_BUFFER here since the allocation
* size varies meaning BUFFERS_GO_ON_STACK would fail */
- char *buf=xmalloc(tftp_bufsize + 4);
-
- tftp_bufsize += 4;
+ /* We must keep the transmit and receive buffers seperate */
+ /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
+ char *xbuf = xmalloc(io_bufsize);
+ char *rbuf = xmalloc(io_bufsize);
+
+ socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+ setsockopt_reuseaddr(socket_fd);
+
+ block_nr = 1;
+ cp = xbuf + 2;
+
+ if (!ENABLE_TFTP || our_lsa) {
+ /* tftpd */
+
+ /* Create a socket which is:
+ * 1. bound to IP:port peer sent 1st datagram to,
+ * 2. connected to peer's IP:port
+ * This way we will answer from the IP:port peer
+ * expects, will not get any other packets on
+ * the socket, and also plain read/write will work. */
+ xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
+ xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
+
+ /* Is there an error already? Send pkt and bail out */
+ if (error_pkt_reason || error_pkt_str[0])
+ goto send_err_pkt;
+
+ if (CMD_GET(option_mask32)) {
+ /* it's upload - we must ACK 1st packet (with filename)
+ * as if it's "block 0" */
+ block_nr = 0;
+ }
- if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
- bb_perror_msg("socket");
- return EXIT_FAILURE;
+ if (user_opt) {
+ struct passwd *pw = xgetpwnam(user_opt);
+ change_identity(pw); /* initgroups, setgid, setuid */
+ }
}
- len = sizeof(sa);
-
- memset(&sa, 0, len);
- bind(socketfd, (struct sockaddr *)&sa, len);
-
- sa.sin_family = host->h_addrtype;
- sa.sin_port = htons(port);
- memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
- sizeof(sa.sin_addr));
-
- /* build opcode */
-
- if (cmd_get) {
- opcode = TFTP_RRQ;
+ /* Open local file (must be after changing user) */
+ if (CMD_PUT(option_mask32)) {
+ open_mode = O_RDONLY;
+ } else {
+ open_mode = O_WRONLY | O_TRUNC | O_CREAT;
+#if ENABLE_TFTPD
+ if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) {
+ /* tftpd without -c */
+ open_mode = O_WRONLY | O_TRUNC;
+ }
+#endif
+ }
+ if (!(option_mask32 & TFTPD_OPT)) {
+ local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
+ if (NOT_LONE_DASH(local_file))
+ local_fd = xopen(local_file, open_mode);
+ } else {
+ local_fd = open(local_file, open_mode);
+ if (local_fd < 0) {
+ error_pkt_reason = ERR_NOFILE;
+ strcpy((char*)error_pkt_str, "can't open file");
+ goto send_err_pkt;
+ }
}
- if (cmd_put) {
+ if (!ENABLE_TFTP || our_lsa) {
+/* gcc 4.3.1 would NOT optimize it out as it should! */
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ if (blksize != TFTP_BLKSIZE_DEFAULT || tsize) {
+ /* Create and send OACK packet. */
+ /* For the download case, block_nr is still 1 -
+ * we expect 1st ACK from peer to be for (block_nr-1),
+ * that is, for "block 0" which is our OACK pkt */
+ opcode = TFTP_OACK;
+ goto add_blksize_opt;
+ }
+#endif
+ } else {
+/* Removing it, or using if() statement instead of #if may lead to
+ * "warning: null argument where non-null required": */
+#if ENABLE_TFTP
+ /* tftp */
+
+ /* We can't (and don't really need to) bind the socket:
+ * we don't know from which local IP datagrams will be sent,
+ * but kernel will pick the same IP every time (unless routing
+ * table is changed), thus peer will see dgrams consistently
+ * coming from the same IP.
+ * We would like to connect the socket, but since peer's
+ * UDP code can be less perfect than ours, _peer's_ IP:port
+ * in replies may differ from IP:port we used to send
+ * our first packet. We can connect() only when we get
+ * first reply. */
+
+ /* build opcode */
opcode = TFTP_WRQ;
+ if (CMD_GET(option_mask32)) {
+ opcode = TFTP_RRQ;
+ }
+ /* add filename and mode */
+ /* fill in packet if the filename fits into xbuf */
+ len = strlen(remote_file) + 1;
+ if (2 + len + sizeof("octet") >= io_bufsize) {
+ bb_error_msg("remote filename is too long");
+ goto ret;
+ }
+ strcpy(cp, remote_file);
+ cp += len;
+ /* add "mode" part of the package */
+ strcpy(cp, "octet");
+ cp += sizeof("octet");
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ if (blksize == TFTP_BLKSIZE_DEFAULT)
+ goto send_pkt;
+
+ /* Non-standard blocksize: add option to pkt */
+ if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
+ bb_error_msg("remote filename is too long");
+ goto ret;
+ }
+ want_option_ack = 1;
+#endif
+#endif /* ENABLE_TFTP */
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ add_blksize_opt:
+#if ENABLE_TFTPD
+ if (tsize) {
+ struct stat st;
+ /* add "tsize", <nul>, size, <nul> */
+ strcpy(cp, "tsize");
+ cp += sizeof("tsize");
+ fstat(local_fd, &st);
+ cp += snprintf(cp, 10, "%u", (int) st.st_size) + 1;
+ }
+#endif
+ if (blksize != TFTP_BLKSIZE_DEFAULT) {
+ /* add "blksize", <nul>, blksize, <nul> */
+ strcpy(cp, "blksize");
+ cp += sizeof("blksize");
+ cp += snprintf(cp, 6, "%d", blksize) + 1;
+ }
+#endif
+ /* First packet is built, so skip packet generation */
+ goto send_pkt;
}
+ /* Using mostly goto's - continue/break will be less clear
+ * in where we actually jump to */
while (1) {
-
- cp = buf;
-
- /* first create the opcode part */
-
- *((unsigned short *) cp) = htons(opcode);
-
+ /* Build ACK or DATA */
+ cp = xbuf + 2;
+ *((uint16_t*)cp) = htons(block_nr);
cp += 2;
-
- /* add filename and mode */
-
- if ((cmd_get && (opcode == TFTP_RRQ)) ||
- (cmd_put && (opcode == TFTP_WRQ))) {
- int too_long = 0;
-
- /* see if the filename fits into buf */
- /* and fill in packet */
-
- len = strlen(remotefile) + 1;
-
- if ((cp + len) >= &buf[tftp_bufsize - 1]) {
- too_long = 1;
- }
- else {
- safe_strncpy(cp, remotefile, len);
- cp += len;
+ block_nr++;
+ opcode = TFTP_ACK;
+ if (CMD_PUT(option_mask32)) {
+ opcode = TFTP_DATA;
+ len = full_read(local_fd, cp, blksize);
+ if (len < 0) {
+ goto send_read_err_pkt;
}
-
- if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
- bb_error_msg("too long remote-filename");
- break;
- }
-
- /* add "mode" part of the package */
-
- memcpy(cp, "octet", 6);
- cp += 6;
-
-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
-
- len = tftp_bufsize - 4; /* data block size */
-
- if (len != TFTP_BLOCKSIZE_DEFAULT) {
-
- if ((&buf[tftp_bufsize - 1] - cp) < 15) {
- bb_error_msg("too long remote-filename");
- break;
- }
-
- /* add "blksize" + number of blocks */
-
- memcpy(cp, "blksize", 8);
- cp += 8;
-
- cp += snprintf(cp, 6, "%d", len) + 1;
-
- want_option_ack = 1;
- }
-#endif
- }
-
- /* add ack and data */
-
- if ((cmd_get && (opcode == TFTP_ACK)) ||
- (cmd_put && (opcode == TFTP_DATA))) {
-
- *((unsigned short *) cp) = htons(block_nr);
-
- cp += 2;
-
- block_nr++;
-
- if (cmd_put && (opcode == TFTP_DATA)) {
- len = read(localfd, cp, tftp_bufsize - 4);
-
- if (len < 0) {
- bb_perror_msg("read");
- break;
- }
-
- if (len != (tftp_bufsize - 4)) {
- finished++;
- }
-
- cp += len;
+ if (len != blksize) {
+ finished = 1;
}
+ cp += len;
}
-
-
- /* send packet */
-
-
- timeout = bb_tftp_num_retries; /* re-initialize */
- do {
-
- len = cp - buf;
-
-#ifdef CONFIG_FEATURE_TFTP_DEBUG
- printf("sending %u bytes\n", len);
- for (cp = buf; cp < &buf[len]; cp++)
- printf("%02x ", *cp);
- printf("\n");
+ send_pkt:
+ /* Send packet */
+ *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
+ send_len = cp - xbuf;
+ /* NB: send_len value is preserved in code below
+ * for potential resend */
+
+ retries = TFTP_NUM_RETRIES; /* re-initialize */
+ waittime_ms = TFTP_TIMEOUT_MS;
+
+ send_again:
+#if ENABLE_TFTP_DEBUG
+ fprintf(stderr, "sending %u bytes\n", send_len);
+ for (cp = xbuf; cp < &xbuf[send_len]; cp++)
+ fprintf(stderr, "%02x ", (unsigned char) *cp);
+ fprintf(stderr, "\n");
#endif
- if (sendto(socketfd, buf, len, 0,
- (struct sockaddr *) &sa, sizeof(sa)) < 0) {
- bb_perror_msg("send");
- len = -1;
- break;
+ xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
+ /* Was it final ACK? then exit */
+ if (finished && (opcode == TFTP_ACK))
+ goto ret;
+
+ recv_again:
+ /* Receive packet */
+ /*pfd[0].fd = socket_fd;*/
+ pfd[0].events = POLLIN;
+ switch (safe_poll(pfd, 1, waittime_ms)) {
+ default:
+ /*bb_perror_msg("poll"); - done in safe_poll */
+ goto ret;
+ case 0:
+ retries--;
+ if (retries == 0) {
+ bb_error_msg("timeout");
+ goto ret; /* no err packet sent */
}
-
- if (finished && (opcode == TFTP_ACK)) {
- break;
+ /* exponential backoff with limit */
+ waittime_ms += waittime_ms/2;
+ if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
+ waittime_ms = TFTP_MAXTIMEOUT_MS;
}
- /* receive packet */
-
- memset(&from, 0, sizeof(from));
- fromlen = sizeof(from);
-
- tv.tv_sec = TFTP_TIMEOUT;
- tv.tv_usec = 0;
-
- FD_ZERO(&rfds);
- FD_SET(socketfd, &rfds);
-
- switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
- case 1:
- len = recvfrom(socketfd, buf, tftp_bufsize, 0,
- (struct sockaddr *) &from, &fromlen);
-
- if (len < 0) {
- bb_perror_msg("recvfrom");
- break;
- }
-
- timeout = 0;
-
- if (sa.sin_port == htons(port)) {
- sa.sin_port = from.sin_port;
- }
- if (sa.sin_port == from.sin_port) {
- break;
- }
-
- /* fall-through for bad packets! */
- /* discard the packet - treat as timeout */
- timeout = bb_tftp_num_retries;
-
- case 0:
- bb_error_msg("timeout");
-
- timeout--;
- if (timeout == 0) {
- len = -1;
- bb_error_msg("last timeout");
- }
- break;
-
- default:
- bb_perror_msg("select");
- len = -1;
+ goto send_again; /* resend last sent pkt */
+ case 1:
+ if (!our_lsa) {
+ /* tftp (not tftpd!) receiving 1st packet */
+ our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */
+ len = recvfrom(socket_fd, rbuf, io_bufsize, 0,
+ &peer_lsa->u.sa, &peer_lsa->len);
+ /* Our first dgram went to port 69
+ * but reply may come from different one.
+ * Remember and use this new port (and IP) */
+ if (len >= 0)
+ xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
+ } else {
+ /* tftpd, or not the very first packet:
+ * socket is connect()ed, can just read from it. */
+ /* Don't full_read()!
+ * This is not TCP, one read == one pkt! */
+ len = safe_read(socket_fd, rbuf, io_bufsize);
+ }
+ if (len < 0) {
+ goto send_read_err_pkt;
+ }
+ if (len < 4) { /* too small? */
+ goto recv_again;
}
-
- } while (timeout && (len >= 0));
-
- if ((finished) || (len < 0)) {
- break;
}
- /* process received packet */
-
-
- opcode = ntohs(*((unsigned short *) buf));
- tmp = ntohs(*((unsigned short *) &buf[2]));
-
-#ifdef CONFIG_FEATURE_TFTP_DEBUG
- printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
+ /* Process recv'ed packet */
+ opcode = ntohs( ((uint16_t*)rbuf)[0] );
+ recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
+#if ENABLE_TFTP_DEBUG
+ fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
#endif
-
if (opcode == TFTP_ERROR) {
- char *msg = NULL;
-
- if (buf[4] != '\0') {
- msg = &buf[4];
- buf[tftp_bufsize - 1] = '\0';
- } else if (tmp < (sizeof(tftp_bb_error_msg)
- / sizeof(char *))) {
-
- msg = (char *) tftp_bb_error_msg[tmp];
- }
-
- if (msg) {
- bb_error_msg("server says: %s", msg);
+ static const char errcode_str[] ALIGN1 =
+ "\0"
+ "file not found\0"
+ "access violation\0"
+ "disk full\0"
+ "bad operation\0"
+ "unknown transfer id\0"
+ "file already exists\0"
+ "no such user\0"
+ "bad option";
+
+ const char *msg = "";
+
+ if (len > 4 && rbuf[4] != '\0') {
+ msg = &rbuf[4];
+ rbuf[io_bufsize - 1] = '\0'; /* paranoia */
+ } else if (recv_blk <= 8) {
+ msg = nth_string(errcode_str, recv_blk);
}
-
- break;
+ bb_error_msg("server error: (%u) %s", recv_blk, msg);
+ goto ret;
}
-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
if (want_option_ack) {
-
- want_option_ack = 0;
-
- if (opcode == TFTP_OACK) {
-
- /* server seems to support options */
-
- char *res;
-
- res = tftp_option_get(&buf[2], len-2,
- "blksize");
-
- if (res) {
- int blksize = atoi(res);
-
- if (tftp_blocksize_check(blksize,
- tftp_bufsize - 4)) {
-
- if (cmd_put) {
- opcode = TFTP_DATA;
- }
- else {
- opcode = TFTP_ACK;
- }
-#ifdef CONFIG_FEATURE_TFTP_DEBUG
- printf("using blksize %u\n", blksize);
-#endif
- tftp_bufsize = blksize + 4;
- block_nr = 0;
- continue;
- }
- }
- /* FIXME:
- * we should send ERROR 8 */
- bb_error_msg("bad server option");
- break;
- }
-
- bb_error_msg("warning: blksize not supported by server"
- " - reverting to 512");
-
- tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
+ want_option_ack = 0;
+ if (opcode == TFTP_OACK) {
+ /* server seems to support options */
+ char *res;
+
+ res = tftp_get_option("blksize", &rbuf[2], len - 2);
+ if (res) {
+ blksize = tftp_blksize_check(res, blksize);
+ if (blksize < 0) {
+ error_pkt_reason = ERR_BAD_OPT;
+ goto send_err_pkt;
+ }
+ io_bufsize = blksize + 4;
+ /* Send ACK for OACK ("block" no: 0) */
+ block_nr = 0;
+ continue;
+ }
+ /* rfc2347:
+ * "An option not acknowledged by the server
+ * must be ignored by the client and server
+ * as if it were never requested." */
+ }
+ bb_error_msg("server only supports blocksize of 512");
+ blksize = TFTP_BLKSIZE_DEFAULT;
+ io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
}
#endif
-
- if (cmd_get && (opcode == TFTP_DATA)) {
-
- if (tmp == block_nr) {
-
- len = write(localfd, &buf[4], len - 4);
-
- if (len < 0) {
- bb_perror_msg("write");
- break;
+ /* block_nr is already advanced to next block# we expect
+ * to get / block# we are about to send next time */
+
+ if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) {
+ if (recv_blk == block_nr) {
+ int sz = full_write(local_fd, &rbuf[4], len - 4);
+ if (sz != len - 4) {
+ strcpy((char*)error_pkt_str, bb_msg_write_error);
+ error_pkt_reason = ERR_WRITE;
+ goto send_err_pkt;
}
-
- if (len != (tftp_bufsize - 4)) {
- finished++;
+ if (sz != blksize) {
+ finished = 1;
}
-
- opcode = TFTP_ACK;
+ continue; /* send ACK */
+ }
+ if (recv_blk == (block_nr - 1)) {
+ /* Server lost our TFTP_ACK. Resend it */
+ block_nr = recv_blk;
continue;
}
}
- if (cmd_put && (opcode == TFTP_ACK)) {
-
- if (tmp == (block_nr - 1)) {
- if (finished) {
- break;
- }
-
- opcode = TFTP_DATA;
- continue;
+ if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
+ /* did peer ACK our last DATA pkt? */
+ if (recv_blk == (uint16_t) (block_nr - 1)) {
+ if (finished)
+ goto ret;
+ continue; /* send next block */
}
}
+ /* Awww... recv'd packet is not recognized! */
+ goto recv_again;
+ /* why recv_again? - rfc1123 says:
+ * "The sender (i.e., the side originating the DATA packets)
+ * must never resend the current DATA packet on receipt
+ * of a duplicate ACK".
+ * DATA pkts are resent ONLY on timeout.
+ * Thus "goto send_again" will ba a bad mistake above.
+ * See:
+ * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
+ */
+ } /* end of "while (1)" */
+ ret:
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ close(local_fd);
+ close(socket_fd);
+ free(xbuf);
+ free(rbuf);
}
-
-#ifdef CONFIG_FEATURE_CLEAN_UP
- close(socketfd);
-
- free(buf);
-#endif
-
- return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+ return finished == 0; /* returns 1 on failure */
+
+ send_read_err_pkt:
+ strcpy((char*)error_pkt_str, bb_msg_read_error);
+ send_err_pkt:
+ if (error_pkt_str[0])
+ bb_error_msg((char*)error_pkt_str);
+ error_pkt[1] = TFTP_ERROR;
+ xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
+ &peer_lsa->u.sa, peer_lsa->len);
+ return EXIT_FAILURE;
+#undef remote_file
+#undef tsize
}
-int tftp_main(int argc, char **argv)
+#if ENABLE_TFTP
+
+int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tftp_main(int argc UNUSED_PARAM, char **argv)
{
- struct hostent *host = NULL;
- char *localfile = NULL;
- char *remotefile = NULL;
- int port = 69;
- int cmd = 0;
- int fd = -1;
- int flags = 0;
- int opt;
+ len_and_sockaddr *peer_lsa;
+ const char *local_file = NULL;
+ const char *remote_file = NULL;
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
+ int blksize;
+#endif
int result;
- int blocksize = TFTP_BLOCKSIZE_DEFAULT;
+ int port;
+ USE_GETPUT(int opt;)
+
+ INIT_G();
+
+ /* -p or -g is mandatory, and they are mutually exclusive */
+ opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
+ USE_GETPUT("g--p:p--g:");
+
+ USE_GETPUT(opt =) getopt32(argv,
+ USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
+ "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
+ &local_file, &remote_file
+ USE_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
+ argv += optind;
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ /* Check if the blksize is valid:
+ * RFC2348 says between 8 and 65464 */
+ blksize = tftp_blksize_check(blksize_str, 65564);
+ if (blksize < 0) {
+ //bb_error_msg("bad block size");
+ return EXIT_FAILURE;
+ }
+#endif
- /* figure out what to pass to getopt */
+ if (remote_file) {
+ if (!local_file) {
+ const char *slash = strrchr(remote_file, '/');
+ local_file = slash ? slash + 1 : remote_file;
+ }
+ } else {
+ remote_file = local_file;
+ }
-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
-#define BS "b:"
-#else
-#define BS
-#endif
+ /* Error if filename or host is not known */
+ if (!remote_file || !argv[0])
+ bb_show_usage();
-#ifdef CONFIG_FEATURE_TFTP_GET
-#define GET "g"
-#else
-#define GET
-#endif
+ port = bb_lookup_port(argv[1], "udp", 69);
+ peer_lsa = xhost2sockaddr(argv[0], port);
-#ifdef CONFIG_FEATURE_TFTP_PUT
-#define PUT "p"
-#else
-#define PUT
+#if ENABLE_TFTP_DEBUG
+ fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
+ xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
+ remote_file, local_file);
#endif
- while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
- switch (opt) {
-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
- case 'b':
- blocksize = atoi(optarg);
- if (!tftp_blocksize_check(blocksize, 0)) {
- return EXIT_FAILURE;
- }
- break;
-#endif
-#ifdef CONFIG_FEATURE_TFTP_GET
- case 'g':
- cmd = tftp_cmd_get;
- flags = O_WRONLY | O_CREAT;
- break;
-#endif
-#ifdef CONFIG_FEATURE_TFTP_PUT
- case 'p':
- cmd = tftp_cmd_put;
- flags = O_RDONLY;
- break;
-#endif
- case 'l':
- localfile = bb_xstrdup(optarg);
- break;
- case 'r':
- remotefile = bb_xstrdup(optarg);
- break;
- }
+ result = tftp_protocol(
+ NULL /*our_lsa*/, peer_lsa,
+ local_file, remote_file
+ USE_FEATURE_TFTP_BLOCKSIZE(USE_TFTPD(, NULL /*tsize*/))
+ USE_FEATURE_TFTP_BLOCKSIZE(, blksize)
+ );
+
+ if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
+ unlink(local_file);
}
+ return result;
+}
+
+#endif /* ENABLE_TFTP */
- if ((cmd == 0) || (optind == argc)) {
+#if ENABLE_TFTPD
+int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tftpd_main(int argc UNUSED_PARAM, char **argv)
+{
+ len_and_sockaddr *our_lsa;
+ len_and_sockaddr *peer_lsa;
+ char *local_file, *mode;
+ const char *error_msg;
+ int opt, result, opcode;
+ USE_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
+ USE_FEATURE_TFTP_BLOCKSIZE(char *tsize = NULL;)
+
+ INIT_G();
+
+ our_lsa = get_sock_lsa(STDIN_FILENO);
+ if (!our_lsa) {
+ /* This is confusing:
+ *bb_error_msg_and_die("stdin is not a socket");
+ * Better: */
bb_show_usage();
+ /* Help text says that tftpd must be used as inetd service,
+ * which is by far the most usual cause of get_sock_lsa
+ * failure */
}
- if(localfile && strcmp(localfile, "-") == 0) {
- fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
+ peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
+ peer_lsa->len = our_lsa->len;
+
+ /* Shifting to not collide with TFTP_OPTs */
+ opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8);
+ argv += optind;
+ if (argv[0])
+ xchdir(argv[0]);
+
+ result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
+ 0 /* flags */,
+ &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
+
+ error_msg = "malformed packet";
+ opcode = ntohs(*(uint16_t*)block_buf);
+ if (result < 4 || result >= sizeof(block_buf)
+ || block_buf[result-1] != '\0'
+ || (USE_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
+ USE_GETPUT(&&)
+ USE_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
+ )
+ ) {
+ goto err;
}
- if(localfile == NULL)
- localfile = remotefile;
- if(remotefile == NULL)
- remotefile = localfile;
- if (fd==-1) {
- fd = open(localfile, flags, 0644);
+ local_file = block_buf + 2;
+ if (local_file[0] == '.' || strstr(local_file, "/.")) {
+ error_msg = "dot in file name";
+ goto err;
}
- if (fd < 0) {
- bb_perror_msg_and_die("local file");
+ mode = local_file + strlen(local_file) + 1;
+ if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
+ goto err;
}
-
- host = xgethostbyname(argv[optind]);
-
- if (optind + 2 == argc) {
- port = atoi(argv[optind + 1]);
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ {
+ char *res;
+ char *opt_str = mode + sizeof("octet");
+ int opt_len = block_buf + result - opt_str;
+ if (opt_len > 0) {
+ res = tftp_get_option("blksize", opt_str, opt_len);
+ if (res) {
+ blksize = tftp_blksize_check(res, 65564);
+ if (blksize < 0) {
+ error_pkt_reason = ERR_BAD_OPT;
+ /* will just send error pkt */
+ goto do_proto;
+ }
+ }
+ /* did client ask us about file size? */
+ tsize = tftp_get_option("tsize", opt_str, opt_len);
+ }
}
-
-#ifdef CONFIG_FEATURE_TFTP_DEBUG
- printf("using server \"%s\", remotefile \"%s\", "
- "localfile \"%s\".\n",
- inet_ntoa(*((struct in_addr *) host->h_addr)),
- remotefile, localfile);
#endif
- result = tftp(cmd, host, remotefile, fd, port, blocksize);
-
-#ifdef CONFIG_FEATURE_CLEAN_UP
- if (!(fd == fileno(stdout) || fd == fileno(stdin))) {
- close(fd);
+ if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
+ if (opt & TFTPD_OPT_r) {
+ /* This would mean "disk full" - not true */
+ /*error_pkt_reason = ERR_WRITE;*/
+ error_msg = bb_msg_write_error;
+ goto err;
+ }
+ USE_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */
+ } else {
+ USE_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
}
-#endif
- return(result);
+
+ /* NB: if error_pkt_str or error_pkt_reason is set up,
+ * tftp_protocol() just sends one error pkt and returns */
+
+ do_proto:
+ close(STDIN_FILENO); /* close old, possibly wildcard socket */
+ /* tftp_protocol() will create new one, bound to particular local IP */
+ result = tftp_protocol(
+ our_lsa, peer_lsa,
+ local_file USE_TFTP(, NULL /*remote_file*/)
+ USE_FEATURE_TFTP_BLOCKSIZE(, tsize)
+ USE_FEATURE_TFTP_BLOCKSIZE(, blksize)
+ );
+
+ return result;
+ err:
+ strcpy((char*)error_pkt_str, error_msg);
+ goto do_proto;
}
+
+#endif /* ENABLE_TFTPD */
+
+#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
diff --git a/release/src/router/busybox/networking/traceroute.c b/release/src/router/busybox/networking/traceroute.c
index 5f8989fd..7284f002 100644
--- a/release/src/router/busybox/networking/traceroute.c
+++ b/release/src/router/busybox/networking/traceroute.c
@@ -1,44 +1,179 @@
-/*-
- * Copyright (c) 1990, 1993
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000
* The Regents of the University of California. All rights reserved.
*
- * This code is derived from software contributed to Berkeley by
- * Van Jacobson.
+ * Busybox port by Vladimir Oleynik (C) 2005 <dzo@simtreas.ru>
*
- * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
* 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.
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* traceroute host - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host. We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway. We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to 30 hops & can be changed with the -m flag). Three
+ * probes (change with -q flag) are sent at each ttl setting and a
+ * line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe. If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed. If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format. We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ * [yak 71]% traceroute nis.nsf.net.
+ * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
+ * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+ * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+ * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+ *
+ * Note that lines 2 & 3 are the same. This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ * [yak 72]% traceroute allspice.lcs.mit.edu.
+ * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+ * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+ * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+ * 12 * * *
+ * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+ * 14 * * *
+ * 15 * * *
+ * 16 * * *
+ * 17 * * *
+ * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us. 14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s. God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram. Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us. The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ * 7 * * *
+ * 8 * * *
+ * 9 * * *
+ * 10 * * *
+ * 11 * * *
+ * 12 * * *
+ * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply. So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length. I.e., rip is really only 7 hops away. A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one). If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
* Notes
* -----
* This program must be run by root or be setuid. (I suggest that
* you *don't* make it setuid -- casual use could result in a lot
* of unnecessary traffic on our poor, congested nets.)
*
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley: A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in a ip datagram). See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
+ * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram. 8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id). So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range). To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example. I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep. I was amazed it ever compiled, much less ran.
+ *
* I stole the idea for this program from Steve Deering. Since
* the first release, I've learned that had I attended the right
* IETF working group meetings, I also could have stolen it from Guy
@@ -54,495 +189,738 @@
* back to yourself. Unfortunately, SO many gateways botch source
* routing, the thing is almost worthless. Maybe one day...
*
- * -- Van Jacobson (van@helios.ee.lbl.gov)
+ * -- Van Jacobson (van@ee.lbl.gov)
* Tue Dec 20 03:50:13 PST 1988
*/
-#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+#define TRACEROUTE_SO_DEBUG 0
+
+/* TODO: undefs were uncommented - ??! we have config system for that! */
+/* probably ok to remove altogether */
+//#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
-#undef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */
-
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include "inet_common.h"
-#include <netdb.h>
-#include <endian.h>
+//#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+//#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+
+
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
-
-#define MAXPACKET 65535 /* max ip packet size */
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
+#include "libbb.h"
+#include "inet_common.h"
+
+#ifndef IPPROTO_ICMP
+# define IPPROTO_ICMP 1
+#endif
+#ifndef IPPROTO_IP
+# define IPPROTO_IP 0
#endif
-/*
- * format of a (udp) probe packet.
- */
-struct opacket {
- struct ip ip;
- struct udphdr udp;
- u_char seq; /* sequence number of this packet */
- u_char ttl; /* ttl packet left with */
- struct timeval tv; /* time packet left */
+/* Keep in sync with getopt32 call! */
+enum {
+ OPT_DONT_FRAGMNT = (1 << 0), /* F */
+ OPT_USE_ICMP = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */
+ OPT_TTL_FLAG = (1 << 2), /* l */
+ OPT_ADDR_NUM = (1 << 3), /* n */
+ OPT_BYPASS_ROUTE = (1 << 4), /* r */
+ OPT_DEBUG = (1 << 5), /* d */
+ OPT_VERBOSE = (1 << 6) * ENABLE_FEATURE_TRACEROUTE_VERBOSE, /* v */
+ OPT_IP_CHKSUM = (1 << 7), /* x */
+ OPT_TOS = (1 << 8), /* t */
+ OPT_DEVICE = (1 << 9), /* i */
+ OPT_MAX_TTL = (1 << 10), /* m */
+ OPT_PORT = (1 << 11), /* p */
+ OPT_NPROBES = (1 << 12), /* q */
+ OPT_SOURCE = (1 << 13), /* s */
+ OPT_WAITTIME = (1 << 14), /* w */
+ OPT_PAUSE_MS = (1 << 15), /* z */
+ OPT_FIRST_TTL = (1 << 16), /* f */
};
+#define verbose (option_mask32 & OPT_VERBOSE)
-/*
- * Definitions for internet protocol version 4.
- * Per RFC 791, September 1981.
- */
-#define IPVERSION 4
-
-
-#include "busybox.h"
+enum {
+ SIZEOF_ICMP_HDR = 8,
+ rcvsock = 3, /* receive (icmp) socket file descriptor */
+ sndsock = 4, /* send (udp/icmp) socket file descriptor */
+};
-static u_char packet[512]; /* last inbound (icmp) packet */
-static struct opacket *outpacket; /* last output (udp) packet */
+/* Data section of the probe packet */
+struct outdata_t {
+ unsigned char seq; /* sequence number of this packet */
+ unsigned char ttl; /* ttl packet left with */
+// UNUSED. Retaining to have the same packet size.
+ struct timeval tv_UNUSED PACKED; /* time packet left */
+};
-static int s; /* receive (icmp) socket file descriptor */
-static int sndsock; /* send (udp) socket file descriptor */
+struct globals {
+ struct ip *outip;
+ struct outdata_t *outdata;
+ len_and_sockaddr *dest_lsa;
+ int packlen; /* total length of packet */
+ int pmtu; /* Path MTU Discovery (RFC1191) */
+ uint16_t ident;
+ uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */
+ int waittime; // 5; /* time to wait for response (in seconds) */
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ int optlen; /* length of ip options */
+#else
+#define optlen 0
+#endif
+ unsigned char recv_pkt[512]; /* last inbound (icmp) packet */
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ /* Maximum number of gateways (include room for one noop) */
+#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
+ /* loose source route gateway list (including room for final destination) */
+ uint32_t gwlist[NGATEWAYS + 1];
+#endif
+};
-static struct sockaddr whereto; /* Who to try to reach */
-static int datalen; /* How much data */
+#define G (*ptr_to_globals)
+#define outip (G.outip )
+#define outdata (G.outdata )
+#define dest_lsa (G.dest_lsa )
+#define packlen (G.packlen )
+#define pmtu (G.pmtu )
+#define ident (G.ident )
+#define port (G.port )
+#define waittime (G.waittime )
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+# define optlen (G.optlen )
+#endif
+#define recv_pkt (G.recv_pkt )
+#define gwlist (G.gwlist )
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+ port = 32768 + 666; \
+ waittime = 5; \
+} while (0)
-static char *hostname;
+#define outicmp ((struct icmp *)(outip + 1))
+#define outudp ((struct udphdr *)(outip + 1))
-static int max_ttl = 30;
-static u_short ident;
-static u_short port = 32768+666; /* start udp dest port # for probe packets */
-#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
-static int verbose;
-#endif
-static int waittime = 5; /* time to wait for response (in seconds) */
-static int nflag; /* print addresses numerically */
+static int
+wait_for_reply(struct sockaddr_in *fromp)
+{
+ struct pollfd pfd[1];
+ int cc = 0;
+ socklen_t fromlen = sizeof(*fromp);
+
+ pfd[0].fd = rcvsock;
+ pfd[0].events = POLLIN;
+ if (safe_poll(pfd, 1, waittime * 1000) > 0)
+ cc = recvfrom(rcvsock, recv_pkt, sizeof(recv_pkt), 0,
+ (struct sockaddr *)fromp, &fromlen);
+ return cc;
+}
/*
- * Construct an Internet address representation.
- * If the nflag has been supplied, give
- * numeric value, otherwise try for symbolic name.
+ * Checksum routine for Internet Protocol family headers (C Version)
*/
-static inline void
-inetname(struct sockaddr_in *from)
+static uint16_t
+in_cksum(uint16_t *addr, int len)
{
- char *cp;
- static char domain[MAXHOSTNAMELEN + 1];
- char name[MAXHOSTNAMELEN + 1];
- static int first = 1;
- const char *ina;
-
- if (first && !nflag) {
- first = 0;
- if (getdomainname(domain, MAXHOSTNAMELEN) != 0)
- domain[0] = 0;
+ int nleft = len;
+ uint16_t *w = addr;
+ uint16_t answer;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
}
- cp = 0;
- if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
- if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) {
- if ((cp = strchr(name, '.')) &&
- !strcmp(cp + 1, domain))
- *cp = 0;
- cp = (char *)name;
- }
- }
- ina = inet_ntoa(from->sin_addr);
- if (nflag)
- printf(" %s", ina);
- else
- printf(" %s (%s)", (cp ? cp : ina), ina);
-}
-
-static inline void
-print(u_char *buf, int cc, struct sockaddr_in *from)
-{
- struct ip *ip;
- int hlen;
- ip = (struct ip *) buf;
- hlen = ip->ip_hl << 2;
- cc -= hlen;
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1)
+ sum += *(unsigned char *)w;
- inetname(from);
-#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
- if (verbose)
- printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
-#endif
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return answer;
}
-static inline double
-deltaT(struct timeval *t1p, struct timeval *t2p)
-{
- double dt;
-
- dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
- (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
- return (dt);
-}
-
-static inline int
-wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
+static void
+send_probe(int seq, int ttl)
{
- fd_set fds;
- static struct timeval wait;
- int cc = 0;
- int fromlen = sizeof (*from);
+ int len, res;
+ void *out;
+
+ /* Payload */
+ outdata->seq = seq;
+ outdata->ttl = ttl;
+// UNUSED: was storing gettimeofday's result there, but never ever checked it
+ /*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/
+
+ if (option_mask32 & OPT_USE_ICMP) {
+ outicmp->icmp_seq = htons(seq);
+
+ /* Always calculate checksum for icmp packets */
+ outicmp->icmp_cksum = 0;
+ outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
+ packlen - (sizeof(*outip) + optlen));
+ if (outicmp->icmp_cksum == 0)
+ outicmp->icmp_cksum = 0xffff;
+ }
- FD_ZERO(&fds);
- FD_SET(sock, &fds);
- if (reset_timer) {
- /*
- * traceroute could hang if someone else has a ping
- * running and our ICMP reply gets dropped but we don't
- * realize it because we keep waking up to handle those
- * other ICMP packets that keep coming in. To fix this,
- * "reset_timer" will only be true if the last packet that
- * came in was for us or if this is the first time we're
- * waiting for a reply since sending out a probe. Note
- * that this takes advantage of the select() feature on
- * Linux where the remaining timeout is written to the
- * struct timeval area.
- */
- wait.tv_sec = waittime;
- wait.tv_usec = 0;
+//BUG! verbose is (x & OPT_VERBOSE), not a counter!
+#if 0 //ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ /* XXX undocumented debugging hack */
+ if (verbose > 1) {
+ const uint16_t *sp;
+ int nshorts, i;
+
+ sp = (uint16_t *)outip;
+ nshorts = (unsigned)packlen / sizeof(uint16_t);
+ i = 0;
+ printf("[ %d bytes", packlen);
+ while (--nshorts >= 0) {
+ if ((i++ % 8) == 0)
+ printf("\n\t");
+ printf(" %04x", ntohs(*sp));
+ sp++;
+ }
+ if (packlen & 1) {
+ if ((i % 8) == 0)
+ printf("\n\t");
+ printf(" %02x", *(unsigned char *)sp);
+ }
+ printf("]\n");
}
+#endif
- if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
- cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
- (struct sockaddr *)from, &fromlen);
+#if defined(IP_TTL)
+ if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ bb_perror_msg_and_die("setsockopt ttl %d", ttl);
+ }
+#endif
- return(cc);
+ len = packlen - sizeof(*outip);
+ if (option_mask32 & OPT_USE_ICMP)
+ out = outicmp;
+ else {
+ out = outdata;
+ len -= sizeof(*outudp);
+ set_nport(dest_lsa, htons(port + seq));
+ }
+ res = xsendto(sndsock, out, len,
+ (struct sockaddr *)&dest_lsa->u.sa, dest_lsa->len);
+ if (res != len) {
+ bb_info_msg("sent %d octets, ret=%d", len, res);
+ }
}
-#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
/*
* Convert an ICMP "type" field to a printable string.
*/
static inline const char *
-pr_type(u_char t)
+pr_type(unsigned char t)
{
- static const char * const ttab[] = {
+ static const char *const ttab[] = {
"Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
"Source Quench", "Redirect", "ICMP 6", "ICMP 7",
- "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
+ "Echo", "Router Advert", "Router Solicit", "Time Exceeded",
"Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
- "Info Reply"
+ "Info Reply", "Mask Request", "Mask Reply"
};
- if(t > 16)
- return("OUT-OF-RANGE");
+ if (t >= ARRAY_SIZE(ttab))
+ return "OUT-OF-RANGE";
- return(ttab[t]);
+ return ttab[t];
}
#endif
-static inline int
-packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
+#if !ENABLE_FEATURE_TRACEROUTE_VERBOSE
+#define packet_ok(cc, from, seq) \
+ packet_ok(cc, seq)
+#endif
+static int
+packet_ok(int cc, const struct sockaddr_in *from, int seq)
{
- struct icmp *icp;
- u_char type, code;
+ const struct icmp *icp;
+ unsigned char type, code;
int hlen;
- struct ip *ip;
+ const struct ip *ip;
- ip = (struct ip *) buf;
+ ip = (struct ip *) recv_pkt;
hlen = ip->ip_hl << 2;
if (cc < hlen + ICMP_MINLEN) {
-#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
if (verbose)
printf("packet too short (%d bytes) from %s\n", cc,
inet_ntoa(from->sin_addr));
#endif
- return (0);
+ return 0;
}
cc -= hlen;
- icp = (struct icmp *)(buf + hlen);
- type = icp->icmp_type; code = icp->icmp_code;
- if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
- type == ICMP_UNREACH) {
- struct ip *hip;
- struct udphdr *up;
+ icp = (struct icmp *)(recv_pkt + hlen);
+ type = icp->icmp_type;
+ code = icp->icmp_code;
+ /* Path MTU Discovery (RFC1191) */
+ pmtu = 0;
+ if (code == ICMP_UNREACH_NEEDFRAG)
+ pmtu = ntohs(icp->icmp_nextmtu);
+
+ if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS)
+ || type == ICMP_UNREACH
+ || type == ICMP_ECHOREPLY
+ ) {
+ const struct ip *hip;
+ const struct udphdr *up;
hip = &icp->icmp_ip;
hlen = hip->ip_hl << 2;
- up = (struct udphdr *)((u_char *)hip + hlen);
- if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
- up->source == htons(ident) &&
- up->dest == htons(port+seq))
- return (type == ICMP_TIMXCEED? -1 : code+1);
+ if (option_mask32 & OPT_USE_ICMP) {
+ struct icmp *hicmp;
+
+ /* XXX */
+ if (type == ICMP_ECHOREPLY
+ && icp->icmp_id == htons(ident)
+ && icp->icmp_seq == htons(seq)
+ ) {
+ return -2;
+ }
+
+ hicmp = (struct icmp *)((unsigned char *)hip + hlen);
+ if (hlen + SIZEOF_ICMP_HDR <= cc
+ && hip->ip_p == IPPROTO_ICMP
+ && hicmp->icmp_id == htons(ident)
+ && hicmp->icmp_seq == htons(seq)
+ ) {
+ return (type == ICMP_TIMXCEED ? -1 : code + 1);
+ }
+ } else {
+ up = (struct udphdr *)((char *)hip + hlen);
+ if (hlen + 12 <= cc
+ && hip->ip_p == IPPROTO_UDP
+// Off: since we do not form the entire IP packet,
+// but defer it to kernel, we can't set source port,
+// and thus can't check it here in the reply
+ /* && up->source == htons(ident) */
+ && up->dest == htons(port + seq)
+ ) {
+ return (type == ICMP_TIMXCEED ? -1 : code + 1);
+ }
+ }
}
-#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
if (verbose) {
int i;
- u_long *lp = (u_long *)&icp->icmp_ip;
+ uint32_t *lp = (uint32_t *)&icp->icmp_ip;
- printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
- cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
+ printf("\n%d bytes from %s to "
+ "%s: icmp type %d (%s) code %d\n",
+ cc, inet_ntoa(from->sin_addr),
+ inet_ntoa(ip->ip_dst),
type, pr_type(type), icp->icmp_code);
- for (i = 4; i < cc ; i += sizeof(long))
- printf("%2d: x%8.8lx\n", i, *lp++);
+ for (i = 4; i < cc; i += sizeof(*lp))
+ printf("%2d: x%8.8x\n", i, *lp++);
}
#endif
- return(0);
+ return 0;
}
-static void /* not inline */
-send_probe(int seq, int ttl)
+/*
+ * Construct an Internet address representation.
+ * If the -n flag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+static void
+print_inetname(const struct sockaddr_in *from)
{
- struct opacket *op = outpacket;
- struct ip *ip = &op->ip;
- struct udphdr *up = &op->udp;
- int i;
- struct timezone tz;
-
- ip->ip_off = 0;
- ip->ip_hl = sizeof(*ip) >> 2;
- ip->ip_p = IPPROTO_UDP;
- ip->ip_len = datalen;
- ip->ip_ttl = ttl;
- ip->ip_v = IPVERSION;
- ip->ip_id = htons(ident+seq);
-
- up->source = htons(ident);
- up->dest = htons(port+seq);
- up->len = htons((u_short)(datalen - sizeof(struct ip)));
- up->check = 0;
-
- op->seq = seq;
- op->ttl = ttl;
- (void) gettimeofday(&op->tv, &tz);
-
- i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
- sizeof(struct sockaddr));
- if (i < 0 || i != datalen) {
- if (i<0)
- perror("sendto");
- printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
- datalen, i);
- (void) fflush(stdout);
+ const char *ina;
+
+ ina = inet_ntoa(from->sin_addr);
+ if (option_mask32 & OPT_ADDR_NUM)
+ printf(" %s", ina);
+ else {
+ char *n = NULL;
+ if (from->sin_addr.s_addr != INADDR_ANY)
+ n = xmalloc_sockaddr2host_noport((struct sockaddr*)from);
+ printf(" %s (%s)", (n ? n : ina), ina);
+ free(n);
}
}
+static void
+print(int cc, const struct sockaddr_in *from)
+{
+ print_inetname(from);
+ if (verbose) {
+ const struct ip *ip;
+ int hlen;
-int
-#ifndef CONFIG_TRACEROUTE
-main(int argc, char *argv[])
-#else
-traceroute_main(int argc, char *argv[])
-#endif
+ ip = (struct ip *) recv_pkt;
+ hlen = ip->ip_hl << 2;
+ cc -= hlen;
+ printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
+ }
+}
+
+static void
+print_delta_ms(unsigned t1p, unsigned t2p)
{
- extern char *optarg;
- extern int optind;
- struct hostent *hp;
- struct sockaddr_in from, *to;
- int ch, i, on, probe, seq, tos, ttl;
-
- int options = 0; /* socket options */
- char *source = 0;
+ unsigned tt = t2p - t1p;
+ printf(" %u.%03u ms", tt / 1000, tt % 1000);
+}
+
+/*
+Usage: [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]
+[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]
+[-w waittime] [-z pausemsecs] host [packetlen]"
+*/
+
+int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int traceroute_main(int argc, char **argv)
+{
+ int minpacket;
+ int ttl, i;
+ int seq = 0;
+ int tos = 0;
+ int max_ttl = 30;
int nprobes = 3;
+ int first_ttl = 1;
+ unsigned pausemsecs = 0;
+ unsigned op;
+ char *source;
+ char *device;
+ char *tos_str;
+ char *max_ttl_str;
+ char *port_str;
+ char *nprobes_str;
+ char *waittime_str;
+ char *pausemsecs_str;
+ char *first_ttl_str;
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ llist_t *source_route_list = NULL;
+ int lsrr = 0;
+#endif
- on = 1;
- seq = tos = 0;
- to = (struct sockaddr_in *)&whereto;
- while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
- switch(ch) {
- case 'd':
-#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
- options |= SO_DEBUG;
+ INIT_G();
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ opt_complementary = "x-x:g::";
+#else
+ opt_complementary = "x-x";
#endif
- break;
- case 'm':
- max_ttl = atoi(optarg);
- if (max_ttl <= 1)
- bb_error_msg_and_die("max ttl must be >1.");
- break;
- case 'n':
- nflag++;
- break;
- case 'p':
- port = atoi(optarg);
- if (port < 1)
- bb_error_msg_and_die("port must be >0.");
- break;
- case 'q':
- nprobes = atoi(optarg);
- if (nprobes < 1)
- bb_error_msg_and_die("nprobes must be >0.");
- break;
- case 'r':
- options |= SO_DONTROUTE;
- break;
- case 's':
- /*
- * set the ip source address of the outbound
- * probe (e.g., on a multi-homed host).
- */
- source = optarg;
- break;
- case 't':
- tos = atoi(optarg);
- if (tos < 0 || tos > 255)
- bb_error_msg_and_die("tos must be 0 to 255.");
- break;
- case 'v':
-#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
- verbose++;
+
+ op = getopt32(argv, "FIlnrdvxt:i:m:p:q:s:w:z:f:"
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ "g:"
#endif
- break;
- case 'w':
- waittime = atoi(optarg);
- if (waittime <= 1)
- bb_error_msg_and_die("wait must be >1 sec.");
- break;
- default:
- bb_show_usage();
+ , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
+ , &source, &waittime_str, &pausemsecs_str, &first_ttl_str
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ , &source_route_list
+#endif
+ );
+
+#if 0 /* IGNORED */
+ if (op & OPT_IP_CHKSUM)
+ bb_error_msg("warning: ip checksums disabled");
+#endif
+ if (op & OPT_TOS)
+ tos = xatou_range(tos_str, 0, 255);
+ if (op & OPT_MAX_TTL)
+ max_ttl = xatou_range(max_ttl_str, 1, 255);
+ if (op & OPT_PORT)
+ port = xatou16(port_str);
+ if (op & OPT_NPROBES)
+ nprobes = xatou_range(nprobes_str, 1, INT_MAX);
+ if (op & OPT_SOURCE) {
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ if (getuid() != 0)
+ bb_error_msg_and_die("you must be root to use -s");
+ }
+ if (op & OPT_WAITTIME)
+ waittime = xatou_range(waittime_str, 1, 24 * 60 * 60);
+ if (op & OPT_PAUSE_MS)
+ pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000);
+ if (op & OPT_FIRST_TTL)
+ first_ttl = xatou_range(first_ttl_str, 1, max_ttl);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ if (source_route_list) {
+ while (source_route_list) {
+ len_and_sockaddr *lsa;
+
+ if (lsrr >= NGATEWAYS)
+ bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
+ lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET);
+ gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr;
+ free(lsa);
+ ++lsrr;
}
- argc -= optind;
- argv += optind;
+ optlen = (lsrr + 1) * sizeof(gwlist[0]);
+ }
+#endif
+
+ minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen;
+ if (!(op & OPT_USE_ICMP))
+ minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR;
+ packlen = minpacket;
- if (argc < 1)
+ /* Process destination and optional packet size */
+ argv += optind;
+ argc -= optind;
+ switch (argc) {
+ case 2:
+ packlen = xatoul_range(argv[1], minpacket, 32 * 1024);
+ /* Fall through */
+ case 1:
+ dest_lsa = xhost2sockaddr(argv[0], port);
+ break;
+ default:
bb_show_usage();
+ }
+
+ /* Ensure the socket fds won't be 0, 1 or 2 */
+ bb_sanitize_stdio();
- setlinebuf (stdout);
-
- memset(&whereto, 0, sizeof(struct sockaddr));
- hp = xgethostbyname(*argv);
- to->sin_family = hp->h_addrtype;
- memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
- hostname = (char *)hp->h_name;
- if (*++argv)
- datalen = atoi(*argv);
- if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
- bb_error_msg_and_die("packet size must be 0 <= s < %d.",
- MAXPACKET - sizeof(struct opacket));
- datalen += sizeof(struct opacket);
- outpacket = (struct opacket *)xmalloc((unsigned)datalen);
- memset(outpacket, 0, datalen);
- outpacket->ip.ip_dst = to->sin_addr;
- outpacket->ip.ip_tos = tos;
- outpacket->ip.ip_v = IPVERSION;
- outpacket->ip.ip_id = 0;
-
- ident = (getpid() & 0xffff) | 0x8000;
-
- if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
- bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
-
- s = create_icmp_socket();
-
-#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
- if (options & SO_DEBUG)
- (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
- (char *)&on, sizeof(on));
+ xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), rcvsock);
+#if TRACEROUTE_SO_DEBUG
+ if (op & OPT_DEBUG)
+ setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
#endif
- if (options & SO_DONTROUTE)
- (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
- (char *)&on, sizeof(on));
+ if (op & OPT_BYPASS_ROUTE)
+ setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ if (op & OPT_USE_ICMP)
+ xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock);
+ else
+ xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+#if defined(IP_OPTIONS)
+ if (lsrr > 0) {
+ unsigned char optlist[MAX_IPOPTLEN];
+
+ /* final hop */
+ gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
+ ++lsrr;
+
+ /* force 4 byte alignment */
+ optlist[0] = IPOPT_NOP;
+ /* loose source route option */
+ optlist[1] = IPOPT_LSRR;
+ i = lsrr * sizeof(gwlist[0]);
+ optlist[2] = i + 3;
+ /* pointer to LSRR addresses */
+ optlist[3] = IPOPT_MINOFF;
+ memcpy(optlist + 4, gwlist, i);
+
+ if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
+ (char *)optlist, i + sizeof(gwlist[0])) < 0) {
+ bb_perror_msg_and_die("IP_OPTIONS");
+ }
+ }
+#endif /* IP_OPTIONS */
+#endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */
#ifdef SO_SNDBUF
- if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
- sizeof(datalen)) < 0)
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
bb_perror_msg_and_die("SO_SNDBUF");
+ }
#endif
-#ifdef IP_HDRINCL
- if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
- sizeof(on)) < 0)
- bb_perror_msg_and_die("IP_HDRINCL");
+#ifdef IP_TOS
+ if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+ bb_perror_msg_and_die("setsockopt tos %d", tos);
+ }
#endif
-#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
- if (options & SO_DEBUG)
- (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
- (char *)&on, sizeof(on));
+#ifdef IP_DONTFRAG
+ if (op & OPT_DONT_FRAGMNT)
+ setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG,
+ &const_int_1, sizeof(const_int_1));
#endif
- if (options & SO_DONTROUTE)
- (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
- (char *)&on, sizeof(on));
-
- if (source) {
- memset(&from, 0, sizeof(struct sockaddr));
- from.sin_family = AF_INET;
- from.sin_addr.s_addr = inet_addr(source);
- if (from.sin_addr.s_addr == -1)
- bb_error_msg_and_die("unknown host %s", source);
- outpacket->ip.ip_src = from.sin_addr;
-#ifndef IP_HDRINCL
- if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
- bb_perror_msg_and_die("bind");
+#if TRACEROUTE_SO_DEBUG
+ if (op & OPT_DEBUG)
+ setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
#endif
+ if (op & OPT_BYPASS_ROUTE)
+ setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ outip = xzalloc(packlen);
+
+ if (op & OPT_USE_ICMP) {
+ ident = getpid() | 0x8000;
+ outicmp->icmp_type = ICMP_ECHO;
+ outicmp->icmp_id = htons(ident);
+ outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR);
+ } else {
+ outdata = (struct outdata_t *)(outudp + 1);
}
- fprintf(stderr, "traceroute to %s (%s)", hostname,
- inet_ntoa(to->sin_addr));
- if (source)
- fprintf(stderr, " from %s", source);
- fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
+ if (op & OPT_DEVICE) /* hmm, do we need error check? */
+ setsockopt_bindtodevice(sndsock, device);
+
+ if (op & OPT_SOURCE) {
+ len_and_sockaddr *source_lsa = xdotted2sockaddr(source, 0);
+ /* Ping does this (why?) */
+ if (setsockopt(sndsock, IPPROTO_IP, IP_MULTICAST_IF,
+ &source_lsa->u.sa, source_lsa->len))
+ bb_error_msg_and_die("can't set multicast source interface");
+//TODO: we can query source port we bound to,
+// and check it in replies... if we care enough
+ xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
+ free(source_lsa);
+ }
- for (ttl = 1; ttl <= max_ttl; ++ttl) {
- u_long lastaddr = 0;
+ /* Revert to non-privileged user after opening sockets */
+ xsetgid(getgid());
+ xsetuid(getuid());
+
+ printf("traceroute to %s (%s)", argv[0],
+ xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa));
+ if (op & OPT_SOURCE)
+ printf(" from %s", source);
+ printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
+
+ for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+//TODO: make it protocol agnostic (get rid of sockaddr_in)
+ struct sockaddr_in from;
+ uint32_t lastaddr = 0;
+ int probe;
+ int unreachable = 0; /* counter */
+ int gotlastaddr = 0; /* flags */
int got_there = 0;
- int unreachable = 0;
+ int first = 1;
- printf("%2d ", ttl);
+ printf("%2d", ttl);
for (probe = 0; probe < nprobes; ++probe) {
- int cc, reset_timer;
- struct timeval t1, t2;
- struct timezone tz;
+ int cc;
+ unsigned t1;
+ unsigned t2;
struct ip *ip;
- (void) gettimeofday(&t1, &tz);
+ if (!first && pausemsecs > 0)
+ usleep(pausemsecs * 1000);
+ fflush(stdout);
+
+ t1 = monotonic_us();
send_probe(++seq, ttl);
- reset_timer = 1;
- while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
- (void) gettimeofday(&t2, &tz);
- if ((i = packet_ok(packet, cc, &from, seq))) {
- reset_timer = 1;
- if (from.sin_addr.s_addr != lastaddr) {
- print(packet, cc, &from);
- lastaddr = from.sin_addr.s_addr;
- }
- printf(" %g ms", deltaT(&t1, &t2));
- switch(i - 1) {
- case ICMP_UNREACH_PORT:
- ip = (struct ip *)packet;
- if (ip->ip_ttl <= 1)
- printf(" !");
- ++got_there;
- break;
- case ICMP_UNREACH_NET:
- ++unreachable;
- printf(" !N");
- break;
- case ICMP_UNREACH_HOST:
- ++unreachable;
- printf(" !H");
- break;
- case ICMP_UNREACH_PROTOCOL:
- ++got_there;
- printf(" !P");
- break;
- case ICMP_UNREACH_NEEDFRAG:
- ++unreachable;
- printf(" !F");
- break;
- case ICMP_UNREACH_SRCFAIL:
- ++unreachable;
- printf(" !S");
- break;
- }
+ first = 0;
+
+ while ((cc = wait_for_reply(&from)) != 0) {
+ t2 = monotonic_us();
+ i = packet_ok(cc, &from, seq);
+ /* Skip short packet */
+ if (i == 0)
+ continue;
+ if (!gotlastaddr
+ || from.sin_addr.s_addr != lastaddr
+ ) {
+ print(cc, &from);
+ lastaddr = from.sin_addr.s_addr;
+ gotlastaddr = 1;
+ }
+ print_delta_ms(t1, t2);
+ ip = (struct ip *)recv_pkt;
+ if (op & OPT_TTL_FLAG)
+ printf(" (%d)", ip->ip_ttl);
+ if (i == -2) {
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ got_there = 1;
+ break;
+ }
+ /* time exceeded in transit */
+ if (i == -1)
+ break;
+ i--;
+ switch (i) {
+ case ICMP_UNREACH_PORT:
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ got_there = 1;
+ break;
+ case ICMP_UNREACH_NET:
+ printf(" !N");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_HOST:
+ printf(" !H");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ printf(" !P");
+ got_there = 1;
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ printf(" !F-%d", pmtu);
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ printf(" !S");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_NET_PROHIB: /* misuse */
+ printf(" !A");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_HOST_PROHIB:
+ printf(" !C");
+ ++unreachable;
break;
- } else
- reset_timer = 0;
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ printf(" !V");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ printf(" !C");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ printf(" !U");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_ISOLATED:
+ printf(" !I");
+ ++unreachable;
+ break;
+ case ICMP_UNREACH_TOSNET:
+ case ICMP_UNREACH_TOSHOST:
+ printf(" !T");
+ ++unreachable;
+ break;
+ default:
+ printf(" !<%d>", i);
+ ++unreachable;
+ break;
+ }
+ break;
}
if (cc == 0)
- printf(" *");
- (void) fflush(stdout);
+ printf(" *");
+ }
+ bb_putchar('\n');
+ if (got_there
+ || (unreachable > 0 && unreachable >= nprobes - 1)
+ ) {
+ break;
}
- putchar('\n');
- if (got_there || unreachable >= nprobes-1)
- return 0;
}
-
return 0;
}
diff --git a/release/src/router/busybox/networking/tunctl.c b/release/src/router/busybox/networking/tunctl.c
new file mode 100644
index 00000000..a8e5270a
--- /dev/null
+++ b/release/src/router/busybox/networking/tunctl.c
@@ -0,0 +1,139 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tun devices controller
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Original code:
+ * Jeff Dike
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include <netinet/in.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include "libbb.h"
+
+/* TUNSETGROUP appeared in 2.6.23 */
+#ifndef TUNSETGROUP
+#define TUNSETGROUP _IOW('T', 206, int)
+#endif
+
+#define IOCTL(a, b, c) ioctl_or_perror_and_die(a, b, c, NULL)
+
+#if 1
+
+int tunctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tunctl_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct ifreq ifr;
+ int fd;
+ const char *opt_name = "tap%d";
+ const char *opt_device = "/dev/net/tun";
+#if ENABLE_FEATURE_TUNCTL_UG
+ const char *opt_user, *opt_group;
+ long user = -1, group = -1;
+#endif
+ unsigned opts;
+
+ enum {
+ OPT_f = 1 << 0, // control device name (/dev/net/tun)
+ OPT_t = 1 << 1, // create named interface
+ OPT_d = 1 << 2, // delete named interface
+#if ENABLE_FEATURE_TUNCTL_UG
+ OPT_u = 1 << 3, // set new interface owner
+ OPT_g = 1 << 4, // set new interface group
+ OPT_b = 1 << 5, // brief output
+#endif
+ };
+
+ opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d
+ opts = getopt32(argv, "f:t:d:" USE_FEATURE_TUNCTL_UG("u:g:b"),
+ &opt_device, &opt_name, &opt_name
+ USE_FEATURE_TUNCTL_UG(, &opt_user, &opt_group));
+
+ // select device
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strncpy_IFNAMSIZ(ifr.ifr_name, opt_name);
+
+ // open device
+ fd = xopen(opt_device, O_RDWR);
+ IOCTL(fd, TUNSETIFF, (void *)&ifr);
+
+ // delete?
+ if (opts & OPT_d) {
+ IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)0);
+ bb_info_msg("Set '%s' %spersistent", ifr.ifr_name, "non");
+ return EXIT_SUCCESS;
+ }
+
+ // create
+#if ENABLE_FEATURE_TUNCTL_UG
+ if (opts & OPT_g) {
+ group = xgroup2gid(opt_group);
+ IOCTL(fd, TUNSETGROUP, (void *)(uintptr_t)group);
+ } else
+ user = geteuid();
+ if (opts & OPT_u)
+ user = xuname2uid(opt_user);
+ IOCTL(fd, TUNSETOWNER, (void *)(uintptr_t)user);
+#endif
+ IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)1);
+
+ // show info
+#if ENABLE_FEATURE_TUNCTL_UG
+ if (opts & OPT_b) {
+ puts(ifr.ifr_name);
+ } else {
+ printf("Set '%s' %spersistent", ifr.ifr_name, "");
+ printf(" and owned by uid %ld", user);
+ if (group != -1)
+ printf(" gid %ld", group);
+ bb_putchar('\n');
+ }
+#else
+ puts(ifr.ifr_name);
+#endif
+ return EXIT_SUCCESS;
+}
+
+#else
+
+/* -210 bytes: */
+
+int tunctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tunctl_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct ifreq ifr;
+ int fd;
+ const char *opt_name = "tap%d";
+ const char *opt_device = "/dev/net/tun";
+ unsigned opts;
+
+ enum {
+ OPT_f = 1 << 0, // control device name (/dev/net/tun)
+ OPT_t = 1 << 1, // create named interface
+ OPT_d = 1 << 2, // delete named interface
+ };
+
+ opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d
+ opts = getopt32(argv, "f:t:d:u:g:b", // u, g, b accepted and ignored
+ &opt_device, &opt_name, &opt_name, NULL, NULL);
+
+ // set interface name
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strncpy_IFNAMSIZ(ifr.ifr_name, opt_name);
+
+ // open device
+ fd = xopen(opt_device, O_RDWR);
+ IOCTL(fd, TUNSETIFF, (void *)&ifr);
+
+ // create or delete interface
+ IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)(0 == (opts & OPT_d)));
+
+ return EXIT_SUCCESS;
+}
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/AUTHORS b/release/src/router/busybox/networking/udhcp/AUTHORS
deleted file mode 100755
index 3772aedb..00000000
--- a/release/src/router/busybox/networking/udhcp/AUTHORS
+++ /dev/null
@@ -1,14 +0,0 @@
-udhcp server/client package
------------------------
-
-Russ Dill <Russ.Dill@asu.edu>
-Matthew Ramsay <matthewr@moreton.com.au>
-Chris Trew <christ@moreton.com.au>
-
-Other Credits:
---------------
-Moreton Bay (http://www.moretonbay.com/)
-Lineo (http://opensource.lineo.com)
-Vladimir Oleynik <dzo@simtrea.ru>, optimize and more integrate for busybox
-
-
diff --git a/release/src/router/busybox/networking/udhcp/COPYING b/release/src/router/busybox/networking/udhcp/COPYING
deleted file mode 100755
index a43ea212..00000000
--- a/release/src/router/busybox/networking/udhcp/COPYING
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 675 Mass Ave, Cambridge, MA 02139, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- Appendix: How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19yy name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/release/src/router/busybox/networking/udhcp/ChangeLog b/release/src/router/busybox/networking/udhcp/ChangeLog
deleted file mode 100755
index 9ee97534..00000000
--- a/release/src/router/busybox/networking/udhcp/ChangeLog
+++ /dev/null
@@ -1,262 +0,0 @@
-0.9.10
- Size optimization (over 3k), more busybox integration
- (Vladimir Oleynik <dzo@simtreas.ru>
-
-0.9.9 (pending)
-+ Fixed a little endian problem in mton (Bastian Blank <waldi@debian.org>)
-+ Fixed a arpping alignment problem (Rui He <rhe@3eti.com>)
-+ Added sanity check for max_leases (udhcp bug #1285) (me)
-+ Finally got rid of the trailing space in enviromental vars (me)
-+ added an new enviromental variable: $mask. It contains the number
- of subnet bits for tools like ip route that require it.
- (Bastian Blank <waldi@debian.org>, me)
-
-0.9.8 (021031)
-+ split up README files (me)
-+ use /dev/urandom to seed xid's (instead of time(0)) (me)
-+ fixed renew behavior (me)
-+ udhcp now fits nicely into busybox
- (Glenn McGrath <bug1@optushome.com.au> as well as myself)
-+ updated client manpage (me)
-+ both client and server now use sockets for signal handling,
- hopefully, this will be the last needed change in signal
- handling, I'm fairly certain all the possible races are now
- closed. (me)
-+ The server now restarts the auto_time timer when it receives
- a SIGUSR1 (write out config file). (me)
-+ Improve signal handling (David Poole)
-+ Fix to config file parsing (Matt Kraai)
-+ Fix load lease logic (me)
-+ Fix clear_lease logic (me)
-+ -h is now an alias for -H (udhcp bug #1253)
-+ Shorter timeout on not receiving offers (me)
-+ Improved signal behavior by client (me)
-+ Would never assign end address (Keith Smith <keith@ksmith.com>)
-+ Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net)
- udhcp bug#1256
-+ Fixed reading of client id (David Poole <davep@portsmith.com>)
-+ change sys_errlist[] to strerror() as it aparently doesn't exist
- (Erik Andersen <andersen@codepoet.org>)
-+ fixed get_raw_packet so it returns -2 on non fatal errors
- (Ted Lemon <Ted.Lemon@nominum.com>)
-+ Improved (hopefully) NAKing behavior (me)
-+ Added -b option (Jouni Malinen)
-+ Compute checksums correctly on big endian hosts
- (Jouni Malinen <jkmaline@cc.hut.fi>)
-
-0.9.7 (020526)
-+ Use add_lease in read_leases, sanitizes leases more, and clears out exprired
- ones if there is no more room (me)
-+ Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747)
-+ Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049)
-+ Added script hook for DHCPNAK (nak), as well as providing the message option
- (me)
-+ Generate the paramaters request list by seeing what options in options.c are
- ored with OPTION_REQ in options.c
-+ Fix dhcp renew forgetfullness on client (bug #1230)
-+ Fix dhcp release bug on client (bug #1231)
-+ Set option request list for DHCP renew (bug #1233)
-+ Set BOOTREQUEST/REPLY properly
-+ Change client-identifier field to popularly expected behavior (me)
-+ Only reopen port on errors (me)
-+ Change fork/close/setsid structures to daemon() (me)
-+ Allow user to specify udhcpd config file at run time (Steven, me)
-+ Write pidfile after changing it (Steven CTR Carr <Steven.CTR.Carr@tc.faa.gov>)
-+ Added env var docs to udhcpc man page (Matt)
-+ Standardized lowercase udhcp in documentation (me)
-+ Accept packets without a UDP checksum (me)
-+ Accept packets with extra garbage (me)
-+ Better error handling in files.c (me)
-+ Combined read_interface function to reduce COMBINED_BINARY size (me)
-+ Drop calc_length(), some servers choke on smaller packets (me)
-+ Try to clean some fat out (me)
-
-0.9.6 (011001)
-+ Added bootp paramaters to server (me)
-+ Added bootp paramaters to client (me)
-+ Added vendor id to client (me)
-+ Better pidfile handling in client and server (me)
-+ Added man pages (Matt Kraai <kraai@alumni.carnegiemellon.edu>)
-
-0.9.5 (010914)
-+ Fixed $HOME and $PATH env passing (me)
-+ Fixed client to only listen for raw packets on correct interface (me)
-+ added --quit,-q option to quit after a lease is obtained (me)
-+ Fixed 100% CPU utilization by client when interface is down (me)
-
-0.9.4 (010827)
-+ Force broadcast to broken clients that request unicast (ie, MSFT 98)
-+ Make install rules (Adam J. Richter <adam@yggdrasil.com>)
-+ One scripts, instead of many (Adam)
-+ Removed script paramater info files (env vars only) (Adam)
-+ Controlling of forking behavior in client (Adam)
-+ General script.c/dhcpc.c cleanups (Adam)
-
-0.9.3 (010820)
-+ Increased debugging verbosity (me)
-+ Cut trailing whitespace when reading config file (me)
-+ added hostname option to client (me)
-+ fixed a strncpy bug in script.c (me)
-+ fixed a leaky socket in dhcpc.c (me)
-+ fixed a leaky socket in dhcpd.c (me)
-
-0.9.2 (010810)
-+ Added raw sockets to client (me)
-+ alignment fixes (Mark Huang)
-+ compiler warning fixes (Mark Huang)
-+ client now sends parameter list (Mark Huang/me)
-+ added ipttl option
-+ Does now not request broadcast packets
-
-0.9.1 (010806)
-+ Added udhcpc client
-+ reorganized functions/files
-+ listening socket now only binds to one interface
-
-0.9.0 (010720) Major rewrite, current changes, goals:
-+ should not segfault on bogus packets.
-+ Options can be read from sname and file fields.
-+ supports all DHCP messages (release, decline, inform).
-+ IP block is now specified by a range of IP's.
-+ Leases file now contains lease time (relative, or absolute).
-+ Just about any DHCP option is now supported.
-+ DNS entries are no longer read from resolv.conf
-+ Lease file can be written periodically when the process receives a SIGUSR1
-+ arpping should be supported on all arches.
-+ support for DHCP relays.
-+ DHCP messages can be unicast if the client requests it.
-+ many, many, many other things.
-
-0.8.29 (000323)
-+ stable(?) release
-
-
-0.8.28 (000323)
-+ removed alarm as it was causing server to go down
-+ removed debugging
-+ break down dhcpd.c into manageable files
-
-
-0.8.27 (000221)
-+ OFFER also sends gateway/subnet (for picky dhcp clients)
-+ multiple DNS now handled from resolv.conf if available
-+ multiple WINS (from dhcpd.conf)
-
-0.8.25 (000120)
-+ now compiles *and* runs on a generic linux system
- tested with a windows 98 client and the sample config
- files in the samples directory.
-
-0.8.24 (000117)
-+ makeiplist tool has basic functionality in place
-+ new sample config files
-+ route add -host 255.255.255.255 dev eth0 added for generic linux
-
-0.8.23 (000117)
-+ NETtel specific fix for ignoring dhcp requests on 2nd interface
-
-0.8.22 (000113)
-+ minor changes to compile under a generic linux system
-+ minor config file location changes for a generic linux system
-+ makeiplist fixes.. still incomplete.. but etting closer
-
-0.8.21 (000113)
-+ now sends the correct server ip instead of hardcoded value
-+ minor debugging fixes for critical messages
-
-0.8.20 (000106)
-+ cut out dhcp server checking.. this was causing dialout ppp
- sessions with idle time set to never time out.
-+ also removed the 10 second pause before launching.. as this
- was originally to stop it replying to a dhcp client
- on a NETtel which was really a bad way to do it in the
- first place :-)
-
-0.8.19 (000104)
-+ fixes for route add -host on a machine that needs to run both
- a DHCP client and server (dual eth box)
-
-0.8.18 (991220)
-
-+ Race conditions fixed by disabling alarm whilst the server is busy
-+ Fixed continous clearing of the offered array so that it is only cleared
- when it is dirty - (could change the position of when dirty is set)
-
-0.8.17 (991212)
-
-- has problems clearing out the offered array
-
-0.8.16 (991203)
-+ Non blocking error is changes to informational as it is not really
- an error
-
-0.8.15 (991129)
-+ Servs the dns field 3 times (Nettel only) so that windows servers
- dont time out whilst nettel is booting
-
-0.8.14 (991126)
-+ added owner check for the offered array so clean out time may be
- increased
-+ added new func to print out chadder/MAC
-
-0.8.13 (991125)
-+ added win95 support (w95 changed xid halfway through conversation)
-+ had to change the offered array to use hardware addresses instead of xid
-+ fixed re offered bug
-+ added more debugging
-
-0.8.12 (991111)
-+ debugging was real bad.. cleaned up a bit.. needs overhaul
-
-
-0.8.11 (991110)
-+ fixed up offeredAddr array to actually be used now!! offeredAddr is
- used to see if another simultaneous connecting client was offered
- an address that we are about to offer another client (multiple
- client bug)
-+ removed re_offered variable as it breaks multiple client support
-+ added lease time to ACK -- doesn't work if in OFFER
-+ decreased internal array clear delay to 60 seconds
-+ minor findAddr bug (returning -1 instead of 0)
-+ if clients xid already in offeredAddr offer the same addr and don't add a
- new addr to offered (caused by a client issuing multiple DISCOVERs)
-
-0.8.10 (991105)
-+ \n bug in arpping
-+ minor debugging changes (removed printfs etc)
-+ started browseiplist (not finished)
-
-0.8.9 (19991105)
-+ fixed options array size bug (options were cut off)
-
-0.8.8 (19991105)
-+ ignores requests from dhcpcd on the same machine
-
-0.8.7 (19991104)
-+ don't die if we can't bind to search for existing DHCP server
-+ slightly more verbose syslogging
-
-0.8.6 (19991103)
-+ added makeiplist (not finished -- core dumps)
-+ minor debug changes
-
-0.8.5 (19991029)
-+ exits if another DHCP server is already on the network
-+ added Linux Makefile
-
-0.8.4 (19991026)
-+ minor bug fix in findaddr preventing an addr being found
-
-0.8.3 (19991025)
-+ fixed up debugging
-+ minor hwaddr issues
-
-0.8.2 (19991022)
-+ free leases (new arpping code from dhcpcd)
-+ fixed bug where crashes if no leases/iplist file
-+ syslogging and debugging switch
-+ serve DNS from resolv.conf
-+ fixed bug where new lease added if same mac offered
-+ now checks the ip is free b4 offering
-+ now supports wins server
-
diff --git a/release/src/router/busybox/networking/udhcp/Config.in b/release/src/router/busybox/networking/udhcp/Config.in
index a3223de9..d4b76e18 100755..100644
--- a/release/src/router/busybox/networking/udhcp/Config.in
+++ b/release/src/router/busybox/networking/udhcp/Config.in
@@ -3,60 +3,120 @@
# see scripts/kbuild/config-language.txt.
#
-menu "udhcp Server/Client"
-
-config CONFIG_UDHCPD
- bool "udhcp Server (udhcpd)"
+config APP_UDHCPD
+ bool "udhcp server (udhcpd)"
default n
help
- uDHCPd is a DHCP server geared primarily toward embedded systems,
+ udhcpd is a DHCP server geared primarily toward embedded systems,
while striving to be fully functional and RFC compliant.
- See http://udhcp.busybox.net for further details.
-
-config CONFIG_UDHCPC
- bool "udhcp Client (udhcpc)"
+config APP_DHCPRELAY
+ bool "dhcprelay"
default n
+ depends on APP_UDHCPD
help
- uDHCPc is a DHCP client geared primarily toward embedded systems,
- while striving to be fully functional and RFC compliant.
-
- The udhcp client negotiates a lease with the DHCP server and
- notifies a set of scripts when a leases is obtained or lost.
-
- See http://udhcp.busybox.net for further details.
+ dhcprelay listens for dhcp requests on one or more interfaces
+ and forwards these requests to a different interface or dhcp
+ server.
-config CONFIG_DUMPLEASES
+config APP_DUMPLEASES
bool "Lease display utility (dumpleases)"
default n
- depends on CONFIG_UDHCPD
+ depends on APP_UDHCPD
help
dumpleases displays the leases written out by the udhcpd server.
Lease times are stored in the file by time remaining in lease, or
by the absolute time that it expires in seconds from epoch.
- See http://udhcp.busybox.net for further details.
+config FEATURE_UDHCPD_WRITE_LEASES_EARLY
+ bool "Rewrite the lease file at every new acknowledge"
+ default n
+ depends on APP_UDHCPD
+ help
+ If selected, udhcpd will write a new file with leases every
+ time a new lease has been accepted, thus eliminating the need
+ to send SIGUSR1 for the initial writing or updating. Any timed
+ rewriting remains undisturbed
+
+config DHCPD_LEASES_FILE
+ string "Absolute path to lease file"
+ default "/var/lib/misc/udhcpd.leases"
+ depends on APP_UDHCPD
+ help
+ udhcpd stores addresses in a lease file. This is the absolute path
+ of the file. Normally it is safe to leave it untouched.
-config CONFIG_FEATURE_UDHCP_SYSLOG
- bool " Log udhcp messages to syslog (instead of stdout)"
+config APP_UDHCPC
+ bool "udhcp client (udhcpc)"
default n
- depends on CONFIG_UDHCPD || CONFIG_UDHCPC
help
- If selected, udhcpd will log all its messages to syslog, otherwise,
- it will attempt to log them to stdout.
+ udhcpc is a DHCP client geared primarily toward embedded systems,
+ while striving to be fully functional and RFC compliant.
- See http://udhcp.busybox.net for further details.
+ The udhcp client negotiates a lease with the DHCP server and
+ runs a script when a lease is obtained or lost.
-config CONFIG_FEATURE_UDHCP_DEBUG
- bool " Compile udhcp with noisy debugging messages"
+config FEATURE_UDHCPC_ARPING
+ bool "Verify that the offered address is free, using ARP ping"
+ default y
+ depends on APP_UDHCPC
+ help
+ If selected, udhcpc will send ARP probes and make sure
+ the offered address is really not in use by anyone. The client
+ will DHCPDECLINE the offer if the address is in use,
+ and restart the discover process.
+
+config FEATURE_UDHCP_PORT
+ bool "Enable '-P port' option for udhcpd and udhcpc"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ help
+ At the cost of ~300 bytes, enables -P port option.
+ This feature is typically not needed.
+
+config UDHCP_DEBUG
+ bool "Compile udhcp with noisy debugging messages"
default n
- depends on CONFIG_UDHCPD || CONFIG_UDHCPC
+ depends on APP_UDHCPD || APP_UDHCPC
help
- If selected, udhcpd will output extra debugging output. If using
- this option, compile uDHCP with "-g", and do not fork the daemon to
- the background.
+ If selected, udhcpd will output extra debugging output.
- See http://udhcp.busybox.net for further details.
+config FEATURE_UDHCP_RFC3397
+ bool "Support for RFC3397 domain search (experimental)"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ help
+ If selected, both client and server will support passing of domain
+ search lists via option 119, specified in RFC3397.
+
+config UDHCPC_DEFAULT_SCRIPT
+ string "Absolute path to config script"
+ default "/usr/share/udhcpc/default.script"
+ depends on APP_UDHCPC
+ help
+ This script is called after udhcpc receives an answer. See
+ examples/udhcp for a working example. Normally it is safe
+ to leave this untouched.
+
+config UDHCPC_SLACK_FOR_BUGGY_SERVERS
+ int "DHCP options slack buffer size"
+ default 80
+ range 0 924
+ depends on APP_UDHCPD || APP_UDHCPC
+ help
+ Some buggy DHCP servers send DHCP offer packets with option
+ field larger than we expect (which might also be considered a
+ buffer overflow attempt). These packets are normally discarded.
+ If circumstances beyond your control force you to support such
+ servers, this may help. The upper limit (924) makes dhcpc accept
+ even 1500 byte packets (maximum-sized ethernet packets).
-endmenu
+ This option does not make dhcp[cd] emit non-standard
+ sized packets.
+ Known buggy DHCP servers:
+ 3Com OfficeConnect Remote 812 ADSL Router:
+ seems to confuse maximum allowed UDP packet size with
+ maximum size of entire IP packet, and sends packets which are
+ 28 bytes too large.
+ Seednet (ISP) VDSL: sends packets 2 bytes too large.
diff --git a/release/src/router/busybox/networking/udhcp/Kbuild b/release/src/router/busybox/networking/udhcp/Kbuild
new file mode 100644
index 00000000..e938076f
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/Kbuild
@@ -0,0 +1,25 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o \
+ signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o \
+ signalpipe.o socket.o
+
+lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
+ script.o
+
+UDHCPC_NEEDS_ARPING-$(CONFIG_FEATURE_UDHCPC_ARPING) = y
+lib-$(UDHCPC_NEEDS_ARPING-y) += arpping.o
+
+lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
+ serverpacket.o static_leases.o
+
+lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
+lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
+lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
diff --git a/release/src/router/busybox/networking/udhcp/Makefile b/release/src/router/busybox/networking/udhcp/Makefile
deleted file mode 100644
index ee34d48f..00000000
--- a/release/src/router/busybox/networking/udhcp/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-TOPDIR:= ../../
-UDHCP_DIR:=./
-include $(TOPDIR).config
-include $(TOPDIR)Rules.mak
-include Makefile.in
-all: $(libraries-y)
--include $(TOPDIR).depend
-
-clean:
- rm -f *.o *.a $(AR_TARGET)
-
diff --git a/release/src/router/busybox/networking/udhcp/Makefile.in b/release/src/router/busybox/networking/udhcp/Makefile.in
deleted file mode 100755
index 502aa1c3..00000000
--- a/release/src/router/busybox/networking/udhcp/Makefile.in
+++ /dev/null
@@ -1,61 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-UDHCP_AR:=udhcp.a
-ifndef $(UDHCP_DIR)
-UDHCP_DIR:=$(TOPDIR)networking/udhcp/
-endif
-
-#ok, so I forgot how to do an or, but this is a quick and dirty hack
-ifeq ($(CONFIG_UDHCPC), y)
-CONFIG_UDHCP_SHARED=y
-else
-ifeq ($(CONFIG_UDHCPD), y)
-CONFIG_UDHCP_SHARED=y
-else
-CONFIG_UDHCP_SHARED=n
-endif
-endif
-
-ifeq ($(CONFIG_UDHCPD), y)
-CONFIG_UDHCP_LEASES_FILE=y
-else
-ifeq ($(CONFIG_UDHCPD), y)
-CONFIG_UDHCP_LEASES_FILE=y
-else
-CONFIG_UDHCP_LEASES_FILE=n
-endif
-endif
-
-UDHCP-y:=
-UDHCP-$(CONFIG_UDHCP_SHARED) += options.c socket.c packet.c common.c
-UDHCP-$(CONFIG_UDHCPC) += dhcpc.c clientpacket.c script.c
-UDHCP-$(CONFIG_UDHCPD) += dhcpd.c arpping.c files.c leases.c serverpacket.c
-UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.c
-UDHCP-$(CONFIG_UDHCP_LEASES_FILE) += leases_file.c
-UDHCP_OBJS=$(patsubst %.c,$(UDHCP_DIR)%.o, $(UDHCP-y))
-
-libraries-y+=$(UDHCP_DIR)$(UDHCP_AR)
-
-$(UDHCP_DIR)$(UDHCP_AR): $(UDHCP_OBJS)
- $(AR) -ro $@ $(UDHCP_OBJS)
-
-$(UDHCP_OBJS): %.o : %.c
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@
-
diff --git a/release/src/router/busybox/networking/udhcp/README b/release/src/router/busybox/networking/udhcp/README
deleted file mode 100644
index 5f4bb78a..00000000
--- a/release/src/router/busybox/networking/udhcp/README
+++ /dev/null
@@ -1,50 +0,0 @@
-udhcp server/client package readme
--------------------------
-
-The udhcp server/client package is primarily geared towards embedded
-systems. It does however, strive to be fully functional, and RFC
-compliant.
-
-
-compile time options
--------------------
-
-The Makefile contains three of the compile time options:
-
- DEBUG: If DEBUG is defined, udhcpd will output extra debugging
- output, compile with -g, and not fork to the background when run.
- SYSLOG: If SYSLOG is defined, udhcpd will log all its messages
- syslog, otherwise, it will attempt to log them to stdout.
-
- COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd,
- is created. If called as udhcpd, the dhcp server will be started.
- If called as udhcpc, the dhcp client will be started.
-
-dhcpd.h contains the other two compile time options:
-
- LEASE_TIME: The default lease time if not specified in the config
- file.
-
- DHCPD_CONFIG_FILE: The defualt config file to use.
-
-options.c contains a set of dhcp options for the client:
-
- name[10]: The name of the option as it will appear in scripts
-
- flags: The type of option, as well as if it will be requested
- by the client (OPTION_REQ)
-
- code: The DHCP code for this option
-
-
-busybox drop-in
---------------
-udhcp is now a drop-in component for busybox (http://busybox.net).
-To update busybox to the latest revision, simply do a:
-
-cp *.[ch] README AUTHORS COPYING ChangeLog TODO \
- <busybox_source>/networking/udhcp
-
-The only two files udhcp does not provide are config.in and
-Makefile.in, so these may need to be updated from time to time.
-
diff --git a/release/src/router/busybox/networking/udhcp/README.dumpleases b/release/src/router/busybox/networking/udhcp/README.dumpleases
deleted file mode 100755
index 6367710c..00000000
--- a/release/src/router/busybox/networking/udhcp/README.dumpleases
+++ /dev/null
@@ -1,17 +0,0 @@
-udhcp lease dump (dumpleases)
-----------------------------
-
-dumpleases displays the leases written out by the udhcpd server. Lease
-times are stored in the file by time remaining in lease (for systems
-without clock that works when there is no power), or by the absolute
-time that it expires in seconds from epoch. dumpleases accepts the
-following command line options:
-
--a, --absolute Interpret lease times as expiration time.
--r, --remaining Interpret lease times as remaining time.
--f, --file=FILE Read lease information from FILE.
--h, --help Display help.
-
-Note that if udhcpd has not written a leases file recently, the output
-of may not be up to date.
-
diff --git a/release/src/router/busybox/networking/udhcp/README.udhcpc b/release/src/router/busybox/networking/udhcp/README.udhcpc
deleted file mode 100755
index 8aee9814..00000000
--- a/release/src/router/busybox/networking/udhcp/README.udhcpc
+++ /dev/null
@@ -1,141 +0,0 @@
-udhcp client (udhcpc)
---------------------
-
-The udhcp client negotiates a lease with the DHCP server and notifies
-a set of scripts when a leases is obtained or lost.
-
-
-command line options
--------------------
-
-The command line options for the udhcp client are:
-
--c, --clientid=CLIENTID Client identifier
--H, --hostname=HOSTNAME Client hostname
--h, Alias for -H
--f, --foreground Do not fork after getting lease
--b, --background Fork to background if lease cannot be
- immediately negotiated.
--i, --interface=INTERFACE Interface to use (default: eth0)
--n, --now Exit with failure if lease cannot be
- immediately negotiated.
--p, --pidfile=file Store process ID of daemon in file
--q, --quit Quit after obtaining lease
--r, --request=IP IP address to request (default: none)
--s, --script=file Run file at dhcp events (default:
- /usr/share/udhcpc/default.script)
--v, --version Display version
-
-
-If the requested IP address cannot be obtained, the client accepts the
-address that the server offers.
-
-
-udhcp client scripts
--------------------
-
-When an event occurs, udhcpc calls the action script. udhcpc never does
-any configuration of the network interface itself, but instead relies on
-a set of scripts. The script by default is
-/usr/share/udhcpc/default.script but this can be changed via the command
-line arguments. The three possible arguments to the script are:
-
- deconfig: This argument is used when udhcpc starts, and
- when a leases is lost. The script must put the interface in an
- up, but deconfigured state, ie: ifconfig $interface 0.0.0.0.
-
- bound: This argument is used when udhcpc moves from an
- unbound, to a bound state. All of the paramaters are set in
- enviromental variables, The script should configure the interface,
- and set any other relavent parameters (default gateway, dns server,
- etc).
-
- renew: This argument is used when a DHCP lease is renewed. All of
- the paramaters are set in enviromental variables. This argument is
- used when the interface is already configured, so the IP address,
- will not change, however, the other DHCP paramaters, such as the
- default gateway, subnet mask, and dns server may change.
-
- nak: This argument is used with udhcpc receives a NAK message.
- The script with the deconfig argument will be called directly
- afterwards, so no changes to the network interface are neccessary.
- This hook is provided for purely informational purposes (the
- message option may contain a reason for the NAK).
-
-The paramaters for enviromental variables are as follows:
-
- $HOME - The set $HOME env or "/"
- $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin"
- $1 - What action the script should perform
- interface - The interface this was obtained on
- ip - The obtained IP
- mask - The number of bits in the netmask (ie: 24)
- siaddr - The bootp next server option
- sname - The bootp server name option
- boot_file - The bootp boot file option
- subnet - The assigend subnet mask
- timezone - Offset in seconds from UTC
- router - A list of routers
- timesvr - A list of time servers
- namesvr - A list of IEN 116 name servers
- dns - A list of DNS server
- logsvr - A list of MIT-LCS UDP log servers
- cookiesvr - A list of RFC 865 cookie servers
- lprsvr - A list of LPR servers
- hostname - The assigned hostname
- bootsize - The length in 512 octect blocks of the bootfile
- domain - The domain name of the network
- swapsvr - The IP address of the client's swap server
- rootpath - The path name of the client's root disk
- ipttl - The TTL to use for this network
- mtu - The MTU to use for this network
- broadcast - The broadcast address for this network
- ntpsrv - A list of NTP servers
- wins - A list of WINS servers
- lease - The lease time, in seconds
- dhcptype - DHCP message type (safely ignored)
- serverid - The IP of the server
- message - Reason for a DHCPNAK
- tftp - The TFTP server name
- bootfile - The bootfile name
-
-additional options are easily added in options.c.
-
-
-note on udhcpc's random seed
----------------------------
-
-udhcpc will seed its random number generator (used for generating xid's)
-by reading /dev/urandom. If you have a lot of embedded systems on the same
-network, with no entropy, you can either seed /dev/urandom by a method of
-your own, or doing the following on startup:
-
-ifconfig eth0 > /dev/urandom
-
-in order to seed /dev/urandom with some data (mac address) unique to your
-system. If reading /dev/urandom fails, udhcpc will fall back to its old
-behavior of seeding with time(0).
-
-
-signals accepted by udhcpc
--------------------------
-
-udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state,
-and SIGUSR2 will force a release of the current lease, and cause udhcpc to
-go into an inactive state (until it is killed, or receives a SIGUSR1). You do
-not need to sleep between sending signals, as signals received are processed
-sequencially in the order they are received.
-
-
-compile time options
--------------------
-
-options.c contains a set of dhcp options for the client:
-
- name[10]: The name of the option as it will appear in scripts
-
- flags: The type of option, as well as if it will be requested
- by the client (OPTION_REQ)
-
- code: The DHCP code for this option
-
diff --git a/release/src/router/busybox/networking/udhcp/README.udhcpd b/release/src/router/busybox/networking/udhcp/README.udhcpd
deleted file mode 100755
index bc6137de..00000000
--- a/release/src/router/busybox/networking/udhcp/README.udhcpd
+++ /dev/null
@@ -1,59 +0,0 @@
-udhcp server (udhcpd)
---------------------
-
-The only command line argument to udhcpd is an optional specifed
-config file. If no config file is specified, udhcpd uses the default
-config file, /etc/udhcpd.conf. Ex:
-
-udhcpd /etc/udhcpd.eth1.conf
-
-The udhcp server employs a number of simple config files:
-
-udhcpd.leases
-------------
-
-The udhcpd.leases behavior is designed for an embedded system. The
-file is written either every auto_time seconds, or when a SIGUSR1
-is received (the auto_time timer restarts if a SIGUSR1 is received).
-If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will
-finish writing the leases file and wait for the aftermentioned script
-to be executed and finish before quiting, so you do not need to sleep
-between sending signals. When the file is written, a script can be
-optionally called to commit the file to flash. Lease times are stored
-in the file by time remaining in lease (for systems without clock
-that works when there is no power), or by the absolute time that it
-expires in seconds from epoch. In the remaining format, expired leases
-are stored as zero. The file is of the format:
-
-16 byte MAC
-4 byte ip address
-u32 expire time
-16 byte MAC
-4 byte ip address
-u32 expire time
-.
-etc.
-
-example: hexdump udhcpd.leases
-
-0000000 1000 c95a 27d9 0000 0000 0000 0000 0000
-0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000
-0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29
-0000030
-
-
-udhcpd.conf
-----------
-
-The format is fairly simple, there is a sample file with all the
-available options and comments describing them in samples/udhcpd.conf
-
-compile time options
--------------------
-
-dhcpd.h contains the other two compile time options:
-
- LEASE_TIME: The default lease time if not specified in the config
- file.
-
- DHCPD_CONFIG_FILE: The defualt config file to use.
diff --git a/release/src/router/busybox/networking/udhcp/TODO b/release/src/router/busybox/networking/udhcp/TODO
deleted file mode 100755
index f88694a8..00000000
--- a/release/src/router/busybox/networking/udhcp/TODO
+++ /dev/null
@@ -1,14 +0,0 @@
-TODO
-----
-+ Integrade README.*'s with manpages
-+ using time(0) breaks if the system clock changes, find a portable solution
-+ make failure of reading functions revert to previous value, not the default
-+ sanity code for option[OPT_LEN]
-+ fix aliasing (ie: eth0:0)
-+ better standard linux distro support
-+ make sure packet generation works on a wide varitey of arches
-+ Interoperability testing
-+ Hooks within the DHCP server
-+ Additional bootp support in client/server
-+ Make serverid option in server configurable
-+ Possibly add failure message to DHCP NAK
diff --git a/release/src/router/busybox/networking/udhcp/arpping.c b/release/src/router/busybox/networking/udhcp/arpping.c
index e20395a9..b10bff65 100644
--- a/release/src/router/busybox/networking/udhcp/arpping.c
+++ b/release/src/router/busybox/networking/udhcp/arpping.c
@@ -1,102 +1,118 @@
+/* vi: set sw=4 ts=4: */
/*
* arpping.c
*
* Mostly stolen from: dhcpcd - DHCP client daemon
* by Yoichi Hariguchi <yoichi@fore.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
-#include <sys/time.h>
-#include <time.h>
-#include <sys/socket.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include "dhcpd.h"
-#include "arpping.h"
#include "common.h"
+#include "dhcpd.h"
-/* args: yiaddr - what IP to ping
- * ip - our ip
- * mac - our arp address
- * interface - interface to use
- * retn: 1 addr free
- * 0 addr used
- * -1 error
- */
-
-/* FIXME: match response against chaddr */
-int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *mac, char *interface)
-{
- int timeout = 2;
- int optval = 1;
- int s; /* socket */
- int rv = 1; /* return value */
- struct sockaddr addr; /* for interface name */
- struct arpMsg arp;
- fd_set fdset;
- struct timeval tm;
- time_t prevTime;
+struct arpMsg {
+ /* Ethernet header */
+ uint8_t h_dest[6]; /* 00 destination ether addr */
+ uint8_t h_source[6]; /* 06 source ether addr */
+ uint16_t h_proto; /* 0c packet type ID field */
+
+ /* ARP packet */
+ uint16_t htype; /* 0e hardware type (must be ARPHRD_ETHER) */
+ uint16_t ptype; /* 10 protocol type (must be ETH_P_IP) */
+ uint8_t hlen; /* 12 hardware address length (must be 6) */
+ uint8_t plen; /* 13 protocol address length (must be 4) */
+ uint16_t operation; /* 14 ARP opcode */
+ uint8_t sHaddr[6]; /* 16 sender's hardware address */
+ uint8_t sInaddr[4]; /* 1c sender's IP address */
+ uint8_t tHaddr[6]; /* 20 target's hardware address */
+ uint8_t tInaddr[4]; /* 26 target's IP address */
+ uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */
+} PACKED;
+enum {
+ ARP_MSG_SIZE = 0x2a
+};
+
+
+/* Returns 1 if no reply received */
+
+int FAST_FUNC arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface)
+{
+ int timeout_ms;
+ struct pollfd pfd[1];
+#define s (pfd[0].fd) /* socket */
+ int rv = 1; /* "no reply received" yet */
+ struct sockaddr addr; /* for interface name */
+ struct arpMsg arp;
- if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) {
- LOG(LOG_ERR, bb_msg_can_not_create_raw_socket);
+ s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+ if (s == -1) {
+ bb_perror_msg(bb_msg_can_not_create_raw_socket);
return -1;
}
-
- if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
- LOG(LOG_ERR, "Could not setsocketopt on raw socket");
- close(s);
- return -1;
+
+ if (setsockopt_broadcast(s) == -1) {
+ bb_perror_msg("cannot enable bcast on raw socket");
+ goto ret;
}
/* send arp request */
memset(&arp, 0, sizeof(arp));
- memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
- memcpy(arp.ethhdr.h_source, mac, 6); /* MAC SA */
- arp.ethhdr.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
- arp.htype = htons(ARPHRD_ETHER); /* hardware type */
- arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
- arp.hlen = 6; /* hardware address length */
- arp.plen = 4; /* protocol address length */
- arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
- memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */
- memcpy(arp.sHaddr, mac, 6); /* source hardware address */
- memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */
-
+ memset(arp.h_dest, 0xff, 6); /* MAC DA */
+ memcpy(arp.h_source, from_mac, 6); /* MAC SA */
+ arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
+ arp.htype = htons(ARPHRD_ETHER); /* hardware type */
+ arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
+ arp.hlen = 6; /* hardware address length */
+ arp.plen = 4; /* protocol address length */
+ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
+ memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */
+ memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
+ /* tHaddr is zero-fiiled */ /* target hardware address */
+ memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */
+
memset(&addr, 0, sizeof(addr));
- strcpy(addr.sa_data, interface);
- if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
- rv = 0;
-
- /* wait arp reply, and check it */
- tm.tv_usec = 0;
- time(&prevTime);
- while (timeout > 0) {
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- tm.tv_sec = timeout;
- if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
- DEBUG(LOG_ERR, "Error on ARPING request: %m");
- if (errno != EINTR) rv = 0;
- } else if (FD_ISSET(s, &fdset)) {
- if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
- if (arp.operation == htons(ARPOP_REPLY) &&
- bcmp(arp.tHaddr, mac, 6) == 0 &&
- *((u_int *) arp.sInaddr) == yiaddr) {
- DEBUG(LOG_INFO, "Valid arp reply receved for this address");
+ safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
+ if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
+ // TODO: error message? caller didn't expect us to fail,
+ // just returning 1 "no reply received" misleads it.
+ goto ret;
+ }
+
+ /* wait for arp reply, and check it */
+ timeout_ms = 2000;
+ do {
+ int r;
+ unsigned prevTime = monotonic_us();
+
+ pfd[0].events = POLLIN;
+ r = safe_poll(pfd, 1, timeout_ms);
+ if (r < 0)
+ break;
+ if (r) {
+ r = read(s, &arp, sizeof(arp));
+ if (r < 0)
+ break;
+ if (r >= ARP_MSG_SIZE
+ && arp.operation == htons(ARPOP_REPLY)
+ /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
+ /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
+ && *((uint32_t *) arp.sInaddr) == test_ip
+ ) {
rv = 0;
break;
}
}
- timeout -= time(NULL) - prevTime;
- time(&prevTime);
- }
+ timeout_ms -= ((unsigned)monotonic_us() - prevTime) / 1000;
+ } while (timeout_ms > 0);
+
+ ret:
close(s);
- DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V");
+ DEBUG("%srp reply received for this address", rv ? "No a" : "A");
return rv;
}
diff --git a/release/src/router/busybox/networking/udhcp/arpping.h b/release/src/router/busybox/networking/udhcp/arpping.h
deleted file mode 100644
index 92b828db..00000000
--- a/release/src/router/busybox/networking/udhcp/arpping.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * arpping .h
- */
-
-#ifndef ARPPING_H
-#define ARPPING_H
-
-#include <netinet/if_ether.h>
-#include <net/if_arp.h>
-#include <net/if.h>
-#include <netinet/in.h>
-
-struct arpMsg {
- struct ethhdr ethhdr; /* Ethernet header */
- u_short htype; /* hardware type (must be ARPHRD_ETHER) */
- u_short ptype; /* protocol type (must be ETH_P_IP) */
- u_char hlen; /* hardware address length (must be 6) */
- u_char plen; /* protocol address length (must be 4) */
- u_short operation; /* ARP opcode */
- u_char sHaddr[6]; /* sender's hardware address */
- u_char sInaddr[4]; /* sender's IP address */
- u_char tHaddr[6]; /* target's hardware address */
- u_char tInaddr[4]; /* target's IP address */
- u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */
-};
-
-/* function prototypes */
-int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *arp, char *interface);
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/clientpacket.c b/release/src/router/busybox/networking/udhcp/clientpacket.c
index 7debac2e..3f9522ff 100644
--- a/release/src/router/busybox/networking/udhcp/clientpacket.c
+++ b/release/src/router/busybox/networking/udhcp/clientpacket.c
@@ -1,28 +1,15 @@
+/* vi: set sw=4 ts=4: */
/* clientpacket.c
*
* Packet generation and dispatching functions for the DHCP client.
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-
-#include <string.h>
-#include <sys/socket.h>
+
#include <features.h>
-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
@@ -30,36 +17,21 @@
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <fcntl.h>
-
+#include "common.h"
#include "dhcpd.h"
-#include "options.h"
#include "dhcpc.h"
-#include "common.h"
+#include "options.h"
/* Create a random xid */
-unsigned long random_xid(void)
+uint32_t FAST_FUNC random_xid(void)
{
- static int initialized;
- if (!initialized) {
- int fd;
- unsigned long seed;
+ static smallint initialized;
- fd = open("/dev/urandom", 0);
- if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) {
- LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m");
- seed = time(0);
- }
- if (fd >= 0) close(fd);
- srand(seed);
- initialized++;
+ if (!initialized) {
+ srand(monotonic_us());
+ initialized = 1;
}
return rand();
}
@@ -68,39 +40,90 @@ unsigned long random_xid(void)
/* initialize a packet with the proper defaults */
static void init_packet(struct dhcpMessage *packet, char type)
{
- struct vendor {
- char vendor, length;
- char str[sizeof("udhcp "VERSION)];
- } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION};
-
- init_header(packet, type);
+ udhcp_init_header(packet, type);
memcpy(packet->chaddr, client_config.arp, 6);
- add_option_string(packet->options, client_config.clientid);
- if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
- add_option_string(packet->options, (unsigned char *) &vendor_id);
+ if (client_config.clientid)
+ add_option_string(packet->options, client_config.clientid);
+ if (client_config.hostname)
+ add_option_string(packet->options, client_config.hostname);
+ if (client_config.fqdn)
+ add_option_string(packet->options, client_config.fqdn);
+ if ((type != DHCPDECLINE) && (type != DHCPRELEASE))
+ add_option_string(packet->options, client_config.vendorclass);
}
-/* Add a paramater request list for stubborn DHCP servers. Pull the data
+/* Add a parameter request list for stubborn DHCP servers. Pull the data
* from the struct in options.c. Don't do bounds checking here because it
* goes towards the head of the packet. */
-static void add_requests(struct dhcpMessage *packet)
+static void add_param_req_option(struct dhcpMessage *packet)
{
+ uint8_t c;
int end = end_option(packet->options);
int i, len = 0;
- packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
- for (i = 0; options[i].code; i++)
- if (options[i].flags & OPTION_REQ)
- packet->options[end + OPT_DATA + len++] = options[i].code;
- packet->options[end + OPT_LEN] = len;
- packet->options[end + OPT_DATA + len] = DHCP_END;
+ for (i = 0; (c = dhcp_options[i].code) != 0; i++) {
+ if (((dhcp_options[i].flags & OPTION_REQ)
+ && !client_config.no_default_options)
+ || (client_config.opt_mask[c >> 3] & (1 << (c & 7)))
+ ) {
+ packet->options[end + OPT_DATA + len] = c;
+ len++;
+ }
+ }
+ if (len) {
+ packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+ packet->options[end + OPT_LEN] = len;
+ packet->options[end + OPT_DATA + len] = DHCP_END;
+ }
+}
+
+/* RFC 2131
+ * 4.4.4 Use of broadcast and unicast
+ *
+ * The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM
+ * messages, unless the client knows the address of a DHCP server.
+ * The client unicasts DHCPRELEASE messages to the server. Because
+ * the client is declining the use of the IP address supplied by the server,
+ * the client broadcasts DHCPDECLINE messages.
+ *
+ * When the DHCP client knows the address of a DHCP server, in either
+ * INIT or REBOOTING state, the client may use that address
+ * in the DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address.
+ * The client may also use unicast to send DHCPINFORM messages
+ * to a known DHCP server. If the client receives no response to DHCP
+ * messages sent to the IP address of a known DHCP server, the DHCP
+ * client reverts to using the IP broadcast address.
+ */
+static int raw_bcast_from_client_config_ifindex(struct dhcpMessage *packet)
+{
+ return udhcp_send_raw_packet(packet,
+ /*src*/ INADDR_ANY, CLIENT_PORT,
+ /*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR,
+ client_config.ifindex);
}
+#if ENABLE_FEATURE_UDHCPC_ARPING
+/* Broadcast a DHCP decline message */
+int FAST_FUNC send_decline(uint32_t xid, uint32_t server, uint32_t requested)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, DHCPDECLINE);
+ packet.xid = xid;
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+ add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+ bb_info_msg("Sending decline...");
+
+ return raw_bcast_from_client_config_ifindex(&packet);
+}
+#endif
+
/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
-int send_discover(unsigned long xid, unsigned long requested)
+int FAST_FUNC send_discover(uint32_t xid, uint32_t requested)
{
struct dhcpMessage packet;
@@ -109,15 +132,22 @@ int send_discover(unsigned long xid, unsigned long requested)
if (requested)
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
- add_requests(&packet);
- LOG(LOG_DEBUG, "Sending discover...");
- return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
- SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+ /* Explicitly saying that we want RFC-compliant packets helps
+ * some buggy DHCP servers to NOT send bigger packets */
+ add_simple_option(packet.options, DHCP_MAX_SIZE, htons(576));
+
+ add_param_req_option(&packet);
+
+ bb_info_msg("Sending discover...");
+ return raw_bcast_from_client_config_ifindex(&packet);
}
/* Broadcasts a DHCP request message */
-int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
+/* RFC 2131 3.1 paragraph 3:
+ * "The client _broadcasts_ a DHCPREQUEST message..."
+ */
+int FAST_FUNC send_select(uint32_t xid, uint32_t server, uint32_t requested)
{
struct dhcpMessage packet;
struct in_addr addr;
@@ -127,121 +157,115 @@ int send_selecting(unsigned long xid, unsigned long server, unsigned long reques
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
add_simple_option(packet.options, DHCP_SERVER_ID, server);
-
- add_requests(&packet);
+ add_param_req_option(&packet);
+
addr.s_addr = requested;
- LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
- return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
- SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+ bb_info_msg("Sending select for %s...", inet_ntoa(addr));
+ return raw_bcast_from_client_config_ifindex(&packet);
}
/* Unicasts or broadcasts a DHCP renew message */
-int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
+int FAST_FUNC send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
{
struct dhcpMessage packet;
- int ret = 0;
init_packet(&packet, DHCPREQUEST);
packet.xid = xid;
packet.ciaddr = ciaddr;
- add_requests(&packet);
- LOG(LOG_DEBUG, "Sending renew...");
- if (server)
- ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
- else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
- SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
- return ret;
-}
+ add_param_req_option(&packet);
+ bb_info_msg("Sending renew...");
+ if (server)
+ return udhcp_send_kernel_packet(&packet,
+ ciaddr, CLIENT_PORT,
+ server, SERVER_PORT);
+
+ return raw_bcast_from_client_config_ifindex(&packet);
+}
/* Unicasts a DHCP release message */
-int send_release(unsigned long server, unsigned long ciaddr)
+int FAST_FUNC send_release(uint32_t server, uint32_t ciaddr)
{
struct dhcpMessage packet;
init_packet(&packet, DHCPRELEASE);
packet.xid = random_xid();
packet.ciaddr = ciaddr;
-
- add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
+
add_simple_option(packet.options, DHCP_SERVER_ID, server);
- LOG(LOG_DEBUG, "Sending release...");
- return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+ bb_info_msg("Sending release...");
+ return udhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
}
-/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
-int get_raw_packet(struct dhcpMessage *payload, int fd)
+/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
+int FAST_FUNC udhcp_recv_raw_packet(struct dhcpMessage *payload, int fd)
{
int bytes;
struct udp_dhcp_packet packet;
- u_int32_t source, dest;
- u_int16_t check;
+ uint16_t check;
- memset(&packet, 0, sizeof(struct udp_dhcp_packet));
- bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
+ memset(&packet, 0, sizeof(packet));
+ bytes = safe_read(fd, &packet, sizeof(packet));
if (bytes < 0) {
- DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
- usleep(500000); /* possible down interface, looping condition */
- return -1;
+ DEBUG("Cannot read on raw listening socket - ignoring");
+ /* NB: possible down interface, etc. Caller should pause. */
+ return bytes; /* returns -1 */
}
-
- if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
- DEBUG(LOG_INFO, "message too short, ignoring");
+
+ if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
+ DEBUG("Packet is too short, ignoring");
return -2;
}
-
+
if (bytes < ntohs(packet.ip.tot_len)) {
- DEBUG(LOG_INFO, "Truncated packet");
+ /* packet is bigger than sizeof(packet), we did partial read */
+ DEBUG("Oversized packet, ignoring");
return -2;
}
-
+
/* ignore any extra garbage bytes */
bytes = ntohs(packet.ip.tot_len);
-
- /* Make sure its the right packet for us, and that it passes sanity checks */
- if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
- packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
- bytes > (int) sizeof(struct udp_dhcp_packet) ||
- ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) {
- DEBUG(LOG_INFO, "unrelated/bogus packet");
- return -2;
+
+ /* make sure its the right packet for us, and that it passes sanity checks */
+ if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION
+ || packet.ip.ihl != (sizeof(packet.ip) >> 2)
+ || packet.udp.dest != htons(CLIENT_PORT)
+ /* || bytes > (int) sizeof(packet) - can't happen */
+ || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
+ ) {
+ DEBUG("Unrelated/bogus packet");
+ return -2;
}
- /* check IP checksum */
+ /* verify IP checksum */
check = packet.ip.check;
packet.ip.check = 0;
- if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
- DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
- return -1;
+ if (check != udhcp_checksum(&packet.ip, sizeof(packet.ip))) {
+ DEBUG("Bad IP header checksum, ignoring");
+ return -2;
}
-
- /* verify the UDP checksum by replacing the header with a psuedo header */
- source = packet.ip.saddr;
- dest = packet.ip.daddr;
+
+ /* verify UDP checksum. IP header has to be modified for this */
+ memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
+ /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
+ packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
check = packet.udp.check;
packet.udp.check = 0;
- memset(&packet.ip, 0, sizeof(packet.ip));
-
- packet.ip.protocol = IPPROTO_UDP;
- packet.ip.saddr = source;
- packet.ip.daddr = dest;
- packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
- if (check && check != checksum(&packet, bytes)) {
- DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
+ if (check && check != udhcp_checksum(&packet, bytes)) {
+ bb_error_msg("packet with bad UDP checksum received, ignoring");
return -2;
}
-
- memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
-
- if (ntohl(payload->cookie) != DHCP_MAGIC) {
- LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
+
+ memcpy(payload, &packet.data, bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
+
+ if (payload->cookie != htonl(DHCP_MAGIC)) {
+ bb_error_msg("received bogus message (bad magic), ignoring");
return -2;
}
- DEBUG(LOG_INFO, "oooooh!!! got some!");
+ DEBUG("Got valid DHCP packet");
return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
-
}
diff --git a/release/src/router/busybox/networking/udhcp/clientpacket.h b/release/src/router/busybox/networking/udhcp/clientpacket.h
deleted file mode 100644
index 2a6facbc..00000000
--- a/release/src/router/busybox/networking/udhcp/clientpacket.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _CLIENTPACKET_H
-#define _CLIENTPACKET_H
-
-unsigned long random_xid(void);
-int send_discover(unsigned long xid, unsigned long requested);
-int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
-int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
-int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
-int send_release(unsigned long server, unsigned long ciaddr);
-int get_raw_packet(struct dhcpMessage *payload, int fd);
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/clientsocket.c b/release/src/router/busybox/networking/udhcp/clientsocket.c
new file mode 100644
index 00000000..1dcc1057
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/clientsocket.c
@@ -0,0 +1,108 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * clientsocket.c -- DHCP client socket creation
+ *
+ * udhcp client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <features.h>
+#include <asm/types.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+#include <linux/filter.h>
+
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+
+int FAST_FUNC udhcp_raw_socket(int ifindex)
+{
+ int fd;
+ struct sockaddr_ll sock;
+
+ /*
+ * Comment:
+ *
+ * I've selected not to see LL header, so BPF doesn't see it, too.
+ * The filter may also pass non-IP and non-ARP packets, but we do
+ * a more complete check when receiving the message in userspace.
+ *
+ * and filter shamelessly stolen from:
+ *
+ * http://www.flamewarmaster.de/software/dhcpclient/
+ *
+ * There are a few other interesting ideas on that page (look under
+ * "Motivation"). Use of netlink events is most interesting. Think
+ * of various network servers listening for events and reconfiguring.
+ * That would obsolete sending HUP signals and/or make use of restarts.
+ *
+ * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
+ * License: GPL v2.
+ *
+ * TODO: make conditional?
+ */
+#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
+ static const struct sock_filter filter_instr[] = {
+ /* check for udp */
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */
+ /* ugly check for arp on ethernet-like and IPv4 */
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */
+ /* skip IP header */
+ BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
+ /* check udp source and destination ports */
+ BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */
+ /* returns */
+ BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */
+ BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
+ };
+ static const struct sock_fprog filter_prog = {
+ .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
+ /* casting const away: */
+ .filter = (struct sock_filter *) filter_instr,
+ };
+
+ DEBUG("opening raw socket on ifindex %d", ifindex);
+
+ fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ DEBUG("got raw socket fd %d", fd);
+
+ if (SERVER_PORT == 67 && CLIENT_PORT == 68) {
+ /* Use only if standard ports are in use */
+ /* Ignoring error (kernel may lack support for this) */
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
+ sizeof(filter_prog)) >= 0)
+ DEBUG("attached filter to raw socket fd %d", fd);
+ }
+
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(ETH_P_IP);
+ sock.sll_ifindex = ifindex;
+ xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
+ DEBUG("bound to raw socket fd %d", fd);
+
+ return fd;
+}
diff --git a/release/src/router/busybox/networking/udhcp/common.c b/release/src/router/busybox/networking/udhcp/common.c
index c01cd576..a47bbaff 100644
--- a/release/src/router/busybox/networking/udhcp/common.c
+++ b/release/src/router/busybox/networking/udhcp/common.c
@@ -1,150 +1,11 @@
+/* vi: set sw=4 ts=4: */
/* common.c
*
- * Functions to assist in the writing and removing of pidfiles.
- *
- * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
- * Rewrited by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/socket.h>
-
#include "common.h"
-
-static int daemonized;
-
-#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
-
-void udhcp_logging(int level, const char *fmt, ...)
-{
- int e = errno;
- va_list p;
- va_list p2;
-
- va_start(p, fmt);
- __va_copy(p2, p);
- if(!daemonized) {
- vprintf(fmt, p);
- putchar('\n');
- errno = e;
- }
- vsyslog(level, fmt, p2);
- va_end(p);
-}
-
-void start_log(const char *client_server)
-{
- openlog(bb_applet_name, LOG_PID | LOG_CONS, LOG_LOCAL0);
- udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
-}
-
-#else
-
-static char *syslog_level_msg[] = {
- [LOG_EMERG] = "EMERGENCY!",
- [LOG_ALERT] = "ALERT!",
- [LOG_CRIT] = "critical!",
- [LOG_WARNING] = "warning",
- [LOG_ERR] = "error",
- [LOG_INFO] = "info",
- [LOG_DEBUG] = "debug"
+const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
-
-void udhcp_logging(int level, const char *fmt, ...)
-{
- int e = errno;
- va_list p;
-
- va_start(p, fmt);
- if(!daemonized) {
- printf("%s, ", syslog_level_msg[level]);
- errno = e;
- vprintf(fmt, p);
- putchar('\n');
- }
- va_end(p);
-}
-
-void start_log(const char *client_server)
-{
- udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
-}
-#endif
-
-static const char *saved_pidfile;
-
-static void exit_fun(void)
-{
- if (saved_pidfile) unlink(saved_pidfile);
-}
-
-void background(const char *pidfile)
-{
- int pid_fd = -1;
-
- if (pidfile) {
- pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
- if (pid_fd < 0) {
- LOG(LOG_ERR, "Unable to open pidfile %s: %m", pidfile);
- } else {
- lockf(pid_fd, F_LOCK, 0);
- if(!saved_pidfile)
- atexit(exit_fun); /* set atexit one only */
- saved_pidfile = pidfile; /* but may be rewrite */
- }
- }
- while (pid_fd >= 0 && pid_fd < 3) pid_fd = dup(pid_fd); /* don't let daemon close it */
- if (daemon(0, 0) == -1) {
- perror("fork");
- exit(1);
- }
- daemonized++;
- if (pid_fd >= 0) {
- FILE *out;
-
- if ((out = fdopen(pid_fd, "w")) != NULL) {
- fprintf(out, "%d\n", getpid());
- fclose(out);
- }
- lockf(pid_fd, F_UNLCK, 0);
- close(pid_fd);
- }
-}
-
-/* Signal handler */
-int udhcp_signal_pipe[2];
-static void signal_handler(int sig)
-{
- if (send(udhcp_signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) {
- LOG(LOG_ERR, "Could not send signal: %m");
- }
-}
-
-void udhcp_set_signal_pipe(int sig_add)
-{
- socketpair(AF_UNIX, SOCK_STREAM, 0, udhcp_signal_pipe);
- signal(SIGUSR1, signal_handler);
- signal(SIGTERM, signal_handler);
- if(sig_add)
- signal(sig_add, signal_handler);
-}
diff --git a/release/src/router/busybox/networking/udhcp/common.h b/release/src/router/busybox/networking/udhcp/common.h
index 768f551b..0cea7aff 100644
--- a/release/src/router/busybox/networking/udhcp/common.h
+++ b/release/src/router/busybox/networking/udhcp/common.h
@@ -1,52 +1,106 @@
+/* vi: set sw=4 ts=4: */
/* common.h
*
- * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
- * Rewrited by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
+#ifndef UDHCP_COMMON_H
+#define UDHCP_COMMON_H 1
+
+#include "libbb.h"
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+#define DEFAULT_SCRIPT CONFIG_UDHCPC_DEFAULT_SCRIPT
+
+extern const uint8_t MAC_BCAST_ADDR[6]; /* six all-ones */
-#include "version.h"
-#include "busybox.h"
-
-#ifndef CONFIG_FEATURE_UDHCP_SYSLOG
-enum syslog_levels {
- LOG_EMERG = 0,
- LOG_ALERT,
- LOG_CRIT,
- LOG_WARNING,
- LOG_ERR,
- LOG_INFO,
- LOG_DEBUG
+/*** packet.h ***/
+
+#define DHCP_OPTIONS_BUFSIZE 308
+
+struct dhcpMessage {
+ uint8_t op; /* 1 = BOOTREQUEST, 2 = BOOTREPLY */
+ uint8_t htype; /* hardware address type. 1 = 10mb ethernet */
+ uint8_t hlen; /* hardware address length */
+ uint8_t hops; /* used by relay agents only */
+ uint32_t xid; /* unique id */
+ uint16_t secs; /* elapsed since client began acquisition/renewal */
+ uint16_t flags; /* only one flag so far: */
+#define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */
+ uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */
+ uint32_t yiaddr; /* 'your' (client) IP address */
+ uint32_t siaddr; /* IP address of next server to use in bootstrap,
+ * returned in DHCPOFFER, DHCPACK by server */
+ uint32_t giaddr; /* relay agent IP address */
+ uint8_t chaddr[16];/* link-layer client hardware address (MAC) */
+ uint8_t sname[64]; /* server host name (ASCIZ) */
+ uint8_t file[128]; /* boot file name (ASCIZ) */
+ uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */
+ uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
+} PACKED;
+
+struct udp_dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct dhcpMessage data;
+} PACKED;
+
+/* Let's see whether compiler understood us right */
+struct BUG_bad_sizeof_struct_udp_dhcp_packet {
+ char BUG_bad_sizeof_struct_udp_dhcp_packet
+ [(sizeof(struct udp_dhcp_packet) != 576 + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS) ? -1 : 1];
};
-#else
-#include <syslog.h>
-#endif
-void start_log(const char *client_server);
-void background(const char *pidfile);
-void udhcp_logging(int level, const char *fmt, ...);
+uint16_t udhcp_checksum(void *addr, int count) FAST_FUNC;
+
+void udhcp_init_header(struct dhcpMessage *packet, char type) FAST_FUNC;
+
+/*int udhcp_recv_raw_packet(struct dhcpMessage *payload, int fd); - in dhcpc.h */
+int udhcp_recv_kernel_packet(struct dhcpMessage *packet, int fd) FAST_FUNC;
+
+int udhcp_send_raw_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port, const uint8_t *dest_arp,
+ int ifindex) FAST_FUNC;
+
+int udhcp_send_kernel_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port) FAST_FUNC;
+
+extern int minpkt; // zzz
-extern int udhcp_signal_pipe[2];
-void udhcp_set_signal_pipe(int sig_add);
+/**/
+void udhcp_run_script(struct dhcpMessage *packet, const char *name) FAST_FUNC;
-#define LOG(level, str, args...) udhcp_logging(level, str, ## args)
+// Still need to clean these up...
-#ifdef CONFIG_FEATURE_UDHCP_DEBUG
-# define DEBUG(level, str, args...) udhcp_logging(level, str, ## args)
+/* from options.h */
+#define get_option udhcp_get_option
+#define end_option udhcp_end_option
+#define add_option_string udhcp_add_option_string
+#define add_simple_option udhcp_add_simple_option
+
+void udhcp_sp_setup(void) FAST_FUNC;
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd) FAST_FUNC;
+int udhcp_sp_read(const fd_set *rfds) FAST_FUNC;
+int udhcp_read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) FAST_FUNC;
+int udhcp_raw_socket(int ifindex) FAST_FUNC;
+int udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf) FAST_FUNC;
+/* Returns 1 if no reply received */
+int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface) FAST_FUNC;
+
+#if ENABLE_UDHCP_DEBUG
+# define DEBUG(str, args...) bb_info_msg("### " str, ## args)
#else
-# define DEBUG(level, str, args...) do {;} while(0)
+# define DEBUG(str, args...) do {;} while (0)
+#endif
+
+POP_SAVED_FUNCTION_VISIBILITY
+
#endif
diff --git a/release/src/router/busybox/networking/udhcp/debug.h b/release/src/router/busybox/networking/udhcp/debug.h
deleted file mode 100644
index a1e19741..00000000
--- a/release/src/router/busybox/networking/udhcp/debug.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _DEBUG_H
-#define _DEBUG_H
-
-#include "libbb_udhcp.h"
-
-#include <stdio.h>
-#ifdef SYSLOG
-#include <syslog.h>
-#endif
-
-
-#ifdef SYSLOG
-# define LOG(level, str, args...) do { printf(str, ## args); \
- printf("\n"); \
- syslog(level, str, ## args); } while(0)
-# define OPEN_LOG(name) openlog(name, 0, 0)
-#define CLOSE_LOG() closelog()
-#else
-# define LOG_EMERG "EMERGENCY!"
-# define LOG_ALERT "ALERT!"
-# define LOG_CRIT "critical!"
-# define LOG_WARNING "warning"
-# define LOG_ERR "error"
-# define LOG_INFO "info"
-# define LOG_DEBUG "debug"
-# define LOG(level, str, args...) do { printf("%s, ", level); \
- printf(str, ## args); \
- printf("\n"); } while(0)
-# define OPEN_LOG(name) do {;} while(0)
-#define CLOSE_LOG() do {;} while(0)
-#endif
-
-#ifdef DEBUG
-# undef DEBUG
-# define DEBUG(level, str, args...) LOG(level, str, ## args)
-# define DEBUGGING
-#else
-# define DEBUG(level, str, args...) do {;} while(0)
-#endif
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/dhcpc.c b/release/src/router/busybox/networking/udhcp/dhcpc.c
index d18cb423..37a26a46 100644
--- a/release/src/router/busybox/networking/udhcp/dhcpc.c
+++ b/release/src/router/busybox/networking/udhcp/dhcpc.c
@@ -1,492 +1,666 @@
+/* vi: set sw=4 ts=4: */
/* dhcpc.c
*
* udhcp DHCP client
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-
-#include <sys/time.h>
-#include <sys/file.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <signal.h>
-#include <time.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <errno.h>
+#include <syslog.h>
+
+/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
+#define WANT_PIDFILE 1
+#include "common.h"
#include "dhcpd.h"
#include "dhcpc.h"
#include "options.h"
-#include "clientpacket.h"
-#include "script.h"
-#include "socket.h"
-#include "common.h"
-static int state;
-static unsigned long requested_ip; /* = 0 */
-static unsigned long server_addr;
-static unsigned long timeout;
-static int packet_num; /* = 0 */
-static int fd = -1;
+
+static int sockfd = -1;
#define LISTEN_NONE 0
#define LISTEN_KERNEL 1
#define LISTEN_RAW 2
-static int listen_mode;
+static smallint listen_mode;
-#ifdef CONFIG_INSTALL_NO_USR
-#define DEFAULT_SCRIPT "/share/udhcpc/default.script"
-#else
-#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
-#endif
+/* initial state: (re)start DHCP negotiation */
+#define INIT_SELECTING 0
+/* discover was sent, DHCPOFFER reply received */
+#define REQUESTING 1
+/* select/renew was sent, DHCPACK reply received */
+#define BOUND 2
+/* half of lease passed, want renew it by sending unicast renew requests */
+#define RENEWING 3
+/* renew requests were not answered, lease is almost over, send broadcast renew */
+#define REBINDING 4
+/* manually requested renew (SIGUSR1) */
+#define RENEW_REQUESTED 5
+/* release, possibly manually requested (SIGUSR2) */
+#define RELEASED 6
+static smallint state;
+
+/* struct client_config_t client_config is in bb_common_bufsiz1 */
-struct client_config_t client_config = {
- /* Default options. */
- abort_if_no_lease: 0,
- foreground: 0,
- quit_after_lease: 0,
- background_if_no_lease: 0,
- interface: "eth0",
- pidfile: NULL,
- script: DEFAULT_SCRIPT,
- clientid: NULL,
- hostname: NULL,
- ifindex: 0,
- arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
-};
/* just a little helper */
-static void change_mode(int new_mode)
+static void change_listen_mode(int new_mode)
{
- DEBUG(LOG_INFO, "entering %s listen mode",
- new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
- if (fd >= 0) close(fd);
- fd = -1;
+ DEBUG("Entering listen mode: %s",
+ new_mode != LISTEN_NONE
+ ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
+ : "none"
+ );
+
listen_mode = new_mode;
+ if (sockfd >= 0) {
+ close(sockfd);
+ sockfd = -1;
+ }
+ if (new_mode == LISTEN_KERNEL)
+ sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT, client_config.interface);
+ else if (new_mode != LISTEN_NONE)
+ sockfd = udhcp_raw_socket(client_config.ifindex);
+ /* else LISTEN_NONE: sockfd stay closed */
}
/* perform a renew */
static void perform_renew(void)
{
- LOG(LOG_INFO, "Performing a DHCP renew");
+ bb_info_msg("Performing a DHCP renew");
switch (state) {
case BOUND:
- change_mode(LISTEN_KERNEL);
+ change_listen_mode(LISTEN_RAW); // zzz
case RENEWING:
case REBINDING:
- state = RENEW_REQUESTED;
- break;
+// state = RENEW_REQUESTED; // zzz
+// break;
case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
- run_script(NULL, "deconfig");
+ udhcp_run_script(NULL, "deconfig");
case REQUESTING:
case RELEASED:
- change_mode(LISTEN_RAW);
+ change_listen_mode(LISTEN_RAW);
state = INIT_SELECTING;
break;
case INIT_SELECTING:
break;
}
-
- /* start things over */
- packet_num = 0;
-
- /* Kill any timeouts because the user wants this to hurry along */
- timeout = 0;
}
/* perform a release */
-static void perform_release(void)
+static void perform_release(uint32_t requested_ip, uint32_t server_addr)
{
- char buffer[16];
+ char buffer[sizeof("255.255.255.255")];
struct in_addr temp_addr;
/* send release packet */
if (state == BOUND || state == RENEWING || state == REBINDING) {
temp_addr.s_addr = server_addr;
- sprintf(buffer, "%s", inet_ntoa(temp_addr));
+ strcpy(buffer, inet_ntoa(temp_addr));
temp_addr.s_addr = requested_ip;
- LOG(LOG_INFO, "Unicasting a release of %s to %s",
+ bb_info_msg("Unicasting a release of %s to %s",
inet_ntoa(temp_addr), buffer);
send_release(server_addr, requested_ip); /* unicast */
- run_script(NULL, "deconfig");
+ udhcp_run_script(NULL, "deconfig");
}
- LOG(LOG_INFO, "Entering released state");
+ bb_info_msg("Entering released state");
- change_mode(LISTEN_NONE);
+ change_listen_mode(LISTEN_NONE);
state = RELEASED;
- timeout = 0x7fffffff;
}
+#if BB_MMU
static void client_background(void)
{
- background(client_config.pidfile);
- client_config.foreground = 1; /* Do not fork again. */
- client_config.background_if_no_lease = 0;
+ bb_daemonize(0);
+ logmode &= ~LOGMODE_STDIO;
+ /* rewrite pidfile, as our pid is different now */
+ write_pidfile(client_config.pidfile);
}
+#endif
-int udhcpc_main(int argc, char *argv[])
+static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
{
- unsigned char *temp, *message;
- unsigned long t1 = 0, t2 = 0, xid = 0;
- unsigned long start = 0, lease;
- fd_set rfds;
+ uint8_t *storage;
+ int len = strlen(str);
+ if (len > 255) len = 255;
+ storage = xzalloc(len + extra + OPT_DATA);
+ storage[OPT_CODE] = code;
+ storage[OPT_LEN] = len + extra;
+ memcpy(storage + extra + OPT_DATA, str, len);
+ return storage;
+}
+
+
+int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int udhcpc_main(int argc UNUSED_PARAM, char **argv)
+{
+ uint8_t *temp, *message;
+ char *str_c, *str_V, *str_h, *str_F, *str_r;
+ USE_FEATURE_UDHCP_PORT(char *str_P;)
+ llist_t *list_O = NULL;
+ int tryagain_timeout = 20;
+ int discover_timeout = 3;
+ int discover_retries = 5;
+ uint32_t server_addr = server_addr; /* for compiler */
+ uint32_t requested_ip = 0;
+ uint32_t xid = 0;
+ uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */
+ int packet_num;
+ int timeout; /* must be signed */
+ unsigned already_waited_sec;
+ unsigned opt;
+ int max_fd;
int retval;
struct timeval tv;
- int c, len;
struct dhcpMessage packet;
- struct in_addr temp_addr;
- time_t now;
- int max_fd;
- int sig;
-
- static const struct option arg_options[] = {
- {"clientid", required_argument, 0, 'c'},
- {"foreground", no_argument, 0, 'f'},
- {"background", no_argument, 0, 'b'},
- {"hostname", required_argument, 0, 'H'},
- {"hostname", required_argument, 0, 'h'},
- {"interface", required_argument, 0, 'i'},
- {"now", no_argument, 0, 'n'},
- {"pidfile", required_argument, 0, 'p'},
- {"quit", no_argument, 0, 'q'},
- {"request", required_argument, 0, 'r'},
- {"script", required_argument, 0, 's'},
- {"version", no_argument, 0, 'v'},
- {0, 0, 0, 0}
+ fd_set rfds;
+
+#if ENABLE_GETOPT_LONG
+ static const char udhcpc_longopts[] ALIGN1 =
+ "clientid\0" Required_argument "c"
+ "clientid-none\0" No_argument "C"
+ "vendorclass\0" Required_argument "V"
+ "hostname\0" Required_argument "H"
+ "fqdn\0" Required_argument "F"
+ "interface\0" Required_argument "i"
+ "now\0" No_argument "n"
+ "pidfile\0" Required_argument "p"
+ "quit\0" No_argument "q"
+ "release\0" No_argument "R"
+ "request\0" Required_argument "r"
+ "script\0" Required_argument "s"
+ "timeout\0" Required_argument "T"
+ "version\0" No_argument "v"
+ "retries\0" Required_argument "t"
+ "tryagain\0" Required_argument "A"
+ "syslog\0" No_argument "S"
+ "request-option\0" Required_argument "O"
+ "no-default-options\0" No_argument "o"
+ "foreground\0" No_argument "f"
+ "background\0" No_argument "b"
+ USE_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")
+ USE_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
+ ;
+#endif
+ enum {
+ OPT_c = 1 << 0,
+ OPT_C = 1 << 1,
+ OPT_V = 1 << 2,
+ OPT_H = 1 << 3,
+ OPT_h = 1 << 4,
+ OPT_F = 1 << 5,
+ OPT_i = 1 << 6,
+ OPT_n = 1 << 7,
+ OPT_p = 1 << 8,
+ OPT_q = 1 << 9,
+ OPT_R = 1 << 10,
+ OPT_r = 1 << 11,
+ OPT_s = 1 << 12,
+ OPT_T = 1 << 13,
+ OPT_t = 1 << 14,
+ OPT_v = 1 << 15,
+ OPT_S = 1 << 16,
+ OPT_A = 1 << 17,
+ OPT_O = 1 << 18,
+ OPT_o = 1 << 19,
+ OPT_f = 1 << 20,
+ OPT_m = 1 << 21, // zzz
+/* The rest has variable bit positions, need to be clever */
+ OPTBIT_LAST = 21,
+ USE_FOR_MMU( OPTBIT_b,)
+ USE_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
+ USE_FEATURE_UDHCP_PORT( OPTBIT_P,)
+ USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,)
+ USE_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
+ USE_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)
};
- /* get options */
- while (1) {
- int option_index = 0;
- c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
- if (c == -1) break;
-
- switch (c) {
- case 'c':
- len = strlen(optarg) > 255 ? 255 : strlen(optarg);
- if (client_config.clientid) free(client_config.clientid);
- client_config.clientid = xmalloc(len + 2);
- client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
- client_config.clientid[OPT_LEN] = len;
- client_config.clientid[OPT_DATA] = '\0';
- strncpy(client_config.clientid + OPT_DATA, optarg, len);
- break;
- case 'f':
- client_config.foreground = 1;
- break;
- case 'b':
- client_config.background_if_no_lease = 1;
- break;
- case 'h':
- case 'H':
- len = strlen(optarg) > 255 ? 255 : strlen(optarg);
- if (client_config.hostname) free(client_config.hostname);
- client_config.hostname = xmalloc(len + 2);
- client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
- client_config.hostname[OPT_LEN] = len;
- strncpy(client_config.hostname + 2, optarg, len);
- break;
- case 'i':
- client_config.interface = optarg;
- break;
- case 'n':
- client_config.abort_if_no_lease = 1;
- break;
- case 'p':
- client_config.pidfile = optarg;
- break;
- case 'q':
- client_config.quit_after_lease = 1;
- break;
- case 'r':
- requested_ip = inet_addr(optarg);
- break;
- case 's':
- client_config.script = optarg;
- break;
- case 'v':
- bb_error_msg("version %s\n", VERSION);
- return(0);
- break;
- default:
- bb_show_usage();
- }
+ /* Default options. */
+ USE_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
+ USE_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
+ client_config.interface = "eth0";
+ client_config.script = DEFAULT_SCRIPT;
+
+ /* Parse command line */
+ /* Cc: mutually exclusive; O: list; -T,-t,-A take numeric param */
+ opt_complementary = "c--C:C--c:O::T+:t+:A+";
+ USE_GETOPT_LONG(applet_long_options = udhcpc_longopts;)
+ opt = getopt32(argv, "c:CV:H:h:F:i:np:qRr:s:T:t:vSA:O:of"
+ "m" // zzz
+ USE_FOR_MMU("b")
+ USE_FEATURE_UDHCPC_ARPING("a")
+ USE_FEATURE_UDHCP_PORT("P:")
+ , &str_c, &str_V, &str_h, &str_h, &str_F
+ , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
+ , &client_config.script /* s */
+ , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
+ , &list_O
+ USE_FEATURE_UDHCP_PORT(, &str_P)
+ );
+ if (opt & OPT_c)
+ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0);
+ if (opt & OPT_V)
+ client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
+ if (opt & (OPT_h|OPT_H))
+ client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
+ if (opt & OPT_F) {
+ client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
+ /* Flags: 0000NEOS
+ S: 1 => Client requests Server to update A RR in DNS as well as PTR
+ O: 1 => Server indicates to client that DNS has been updated regardless
+ E: 1 => Name data is DNS format, i.e. <4>host<6>domain<3>com<0> not "host.domain.com"
+ N: 1 => Client requests Server to not update DNS
+ */
+ client_config.fqdn[OPT_DATA + 0] = 0x1;
+ /* client_config.fqdn[OPT_DATA + 1] = 0; - redundant */
+ /* client_config.fqdn[OPT_DATA + 2] = 0; - redundant */
+ }
+ if (opt & OPT_r)
+ requested_ip = inet_addr(str_r);
+ if (opt & OPT_v) {
+ puts("version "BB_VER);
+ return 0;
+ }
+#if ENABLE_FEATURE_UDHCP_PORT
+ if (opt & OPT_P) {
+ CLIENT_PORT = xatou16(str_P);
+ SERVER_PORT = CLIENT_PORT - 1;
+ }
+#endif
+ if (opt & OPT_o)
+ client_config.no_default_options = 1;
+ while (list_O) {
+ char *optstr = llist_pop(&list_O);
+ int n = index_in_strings(dhcp_option_strings, optstr);
+ if (n < 0)
+ bb_error_msg_and_die("unknown option '%s'", optstr);
+ n = dhcp_options[n].code;
+ client_config.opt_mask[n >> 3] |= 1 << (n & 7);
+ }
+ if (opt & OPT_m) minpkt = 1; // zzz
+
+ if (udhcp_read_interface(client_config.interface, &client_config.ifindex,
+ NULL, client_config.arp))
+ return 1;
+#if !BB_MMU
+ /* on NOMMU reexec (i.e., background) early */
+ if (!(opt & OPT_f)) {
+ bb_daemonize_or_rexec(0 /* flags */, argv);
+ logmode = LOGMODE_NONE;
+ }
+#endif
+ if (opt & OPT_S) {
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode |= LOGMODE_SYSLOG;
}
- start_log("client");
- if (read_interface(client_config.interface, &client_config.ifindex,
- NULL, client_config.arp) < 0)
- return(1);
-
- if (!client_config.clientid) {
- client_config.clientid = xmalloc(6 + 3);
- client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
- client_config.clientid[OPT_LEN] = 7;
+ /* Make sure fd 0,1,2 are open */
+ bb_sanitize_stdio();
+ /* Equivalent of doing a fflush after every \n */
+ setlinebuf(stdout);
+
+ /* Create pidfile */
+ write_pidfile(client_config.pidfile);
+
+ /* Goes to stdout (unless NOMMU) and possibly syslog */
+ bb_info_msg("%s (v"BB_VER") started", applet_name);
+
+ /* if not set, and not suppressed, setup the default client ID */
+ if (!client_config.clientid && !(opt & OPT_C)) {
+ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
client_config.clientid[OPT_DATA] = 1;
- memcpy(client_config.clientid + 3, client_config.arp, 6);
+ memcpy(client_config.clientid + OPT_DATA+1, client_config.arp, 6);
}
- /* setup signal handlers */
- udhcp_set_signal_pipe(SIGUSR2);
-
+ if (!client_config.vendorclass)
+ client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0);
+
+ /* setup the signal pipe */
+ udhcp_sp_setup();
+
state = INIT_SELECTING;
- run_script(NULL, "deconfig");
- change_mode(LISTEN_RAW);
+ udhcp_run_script(NULL, "deconfig");
+ change_listen_mode(LISTEN_RAW);
+ packet_num = 0;
+ timeout = 0;
+ already_waited_sec = 0;
+ /* Main event loop. select() waits on signal pipe and possibly
+ * on sockfd.
+ * "continue" statements in code below jump to the top of the loop.
+ */
for (;;) {
+ /* silence "uninitialized!" warning */
+ unsigned timestamp_before_wait = timestamp_before_wait;
- tv.tv_sec = timeout - time(0);
- tv.tv_usec = 0;
- FD_ZERO(&rfds);
+ //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);
- if (listen_mode != LISTEN_NONE && fd < 0) {
- if (listen_mode == LISTEN_KERNEL)
- fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
- else
- fd = raw_socket(client_config.ifindex);
- if (fd < 0) {
- LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
- return(0);
- }
- }
- if (fd >= 0) FD_SET(fd, &rfds);
- FD_SET(udhcp_signal_pipe[0], &rfds);
+ /* Was opening raw or udp socket here
+ * if (listen_mode != LISTEN_NONE && sockfd < 0),
+ * but on fast network renew responses return faster
+ * than we open sockets. Thus this code is moved
+ * to change_listen_mode(). Thus we open listen socket
+ * BEFORE we send renew request (see "case BOUND:"). */
+
+ max_fd = udhcp_sp_fd_set(&rfds, sockfd);
- if (tv.tv_sec > 0) {
- DEBUG(LOG_INFO, "Waiting on select...\n");
- max_fd = udhcp_signal_pipe[0] > fd ? udhcp_signal_pipe[0] : fd;
+ tv.tv_sec = timeout - already_waited_sec;
+ tv.tv_usec = 0;
+ retval = 0; /* If we already timed out, fall through, else... */
+ if ((int)tv.tv_sec > 0) {
+ timestamp_before_wait = (unsigned)monotonic_sec();
+ DEBUG("Waiting on select...");
retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
- } else retval = 0; /* If we already timed out, fall through */
+ if (retval < 0) {
+ /* EINTR? A signal was caught, don't panic */
+ if (errno == EINTR) {
+ already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
+ continue;
+ }
+ /* Else: an error occured, panic! */
+ bb_perror_msg_and_die("select");
+ }
+ }
- now = time(0);
+ /* If timeout dropped to zero, time to become active:
+ * resend discover/renew/whatever
+ */
if (retval == 0) {
- /* timeout dropped to zero */
+ /* We will restart the wait in any case */
+ already_waited_sec = 0;
+
switch (state) {
case INIT_SELECTING:
- if (packet_num < 3) {
+ if (packet_num < discover_retries) {
if (packet_num == 0)
xid = random_xid();
- /* send discover packet */
send_discover(xid, requested_ip); /* broadcast */
-
- timeout = now + ((packet_num == 2) ? 4 : 2);
+
+ timeout = discover_timeout;
packet_num++;
- } else {
- if (client_config.background_if_no_lease) {
- LOG(LOG_INFO, "No lease, forking to background.");
- client_background();
- } else if (client_config.abort_if_no_lease) {
- LOG(LOG_INFO, "No lease, failing.");
- return(1);
- }
- /* wait to try again */
- packet_num = 0;
- timeout = now + 60;
+ continue;
}
- break;
- case RENEW_REQUESTED:
+ leasefail:
+ udhcp_run_script(NULL, "leasefail");
+#if BB_MMU /* -b is not supported on NOMMU */
+ if (opt & OPT_b) { /* background if no lease */
+ bb_info_msg("No lease, forking to background");
+ client_background();
+ /* do not background again! */
+ opt = ((opt & ~OPT_b) | OPT_f);
+ } else
+#endif
+ if (opt & OPT_n) { /* abort if no lease */
+ bb_info_msg("No lease, failing");
+ retval = 1;
+ goto ret;
+ }
+ /* wait before trying again */
+ timeout = tryagain_timeout;
+ packet_num = 0;
+ continue;
case REQUESTING:
- if (packet_num < 3) {
- /* send request packet */
- if (state == RENEW_REQUESTED)
- send_renew(xid, server_addr, requested_ip); /* unicast */
- else send_selecting(xid, server_addr, requested_ip); /* broadcast */
-
- timeout = now + ((packet_num == 2) ? 10 : 2);
+ if (packet_num < discover_retries) {
+ /* send broadcast select packet */
+ send_select(xid, server_addr, requested_ip);
+ timeout = discover_timeout;
packet_num++;
- } else {
- /* timed out, go back to init state */
- if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
- state = INIT_SELECTING;
- timeout = now;
- packet_num = 0;
- change_mode(LISTEN_RAW);
+ continue;
}
- break;
+ /* Timed out, go back to init state.
+ * "discover...select...discover..." loops
+ * were seen in the wild. Treat them similarly
+ * to "no response to discover" case */
+ change_listen_mode(LISTEN_RAW);
+ state = INIT_SELECTING;
+ goto leasefail;
case BOUND:
- /* Lease is starting to run out, time to enter renewing state */
+ /* Half of the lease passed, time to enter renewing state */
state = RENEWING;
- change_mode(LISTEN_KERNEL);
- DEBUG(LOG_INFO, "Entering renew state");
+ change_listen_mode(LISTEN_RAW); // was: LISTEN_KERNEL -- zzz
+ DEBUG("Entering renew state");
/* fall right through */
+ case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
+ case_RENEW_REQUESTED:
case RENEWING:
- /* Either set a new T1, or enter REBINDING state */
- if ((t2 - t1) <= (lease / 14400 + 1)) {
- /* timed out, enter rebinding state */
- state = REBINDING;
- timeout = now + (t2 - t1);
- DEBUG(LOG_INFO, "Entering rebinding state");
- } else {
- /* send a request packet */
- send_renew(xid, server_addr, requested_ip); /* unicast */
-
- t1 = (t2 - t1) / 2 + t1;
- timeout = t1 + start;
+ if (timeout > 60) {
+ /* send an unicast renew request packet */
+ send_renew(xid, server_addr, requested_ip);
+ timeout >>= 1;
+ continue;
}
- break;
+ /* Timed out, enter rebinding state */
+ DEBUG("Entering rebinding state");
+ state = REBINDING;
+ /* fall right through */
case REBINDING:
- /* Either set a new T2, or enter INIT state */
- if ((lease - t2) <= (lease / 14400 + 1)) {
- /* timed out, enter init state */
- state = INIT_SELECTING;
- LOG(LOG_INFO, "Lease lost, entering init state");
- run_script(NULL, "deconfig");
- timeout = now;
- packet_num = 0;
- change_mode(LISTEN_RAW);
- } else {
+ /* Switch to bcast receive */
+ change_listen_mode(LISTEN_RAW);
+ /* Lease is *really* about to run out,
+ * try to find DHCP server using broadcast */
+ if (timeout > 0) {
/* send a request packet */
- send_renew(xid, 0, requested_ip); /* broadcast */
-
- t2 = (lease - t2) / 2 + t2;
- timeout = t2 + start;
+ send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); /* broadcast */
+ timeout >>= 1;
+ continue;
}
- break;
- case RELEASED:
- /* yah, I know, *you* say it would never happen */
- timeout = 0x7fffffff;
- break;
+ /* Timed out, enter init state */
+ bb_info_msg("Lease lost, entering init state");
+ udhcp_run_script(NULL, "deconfig");
+ state = INIT_SELECTING;
+ /*timeout = 0; - already is */
+ packet_num = 0;
+ continue;
+ /* case RELEASED: */
}
- } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
- /* a packet is ready, read it */
-
+ /* yah, I know, *you* say it would never happen */
+ timeout = INT_MAX;
+ continue; /* back to main loop */
+ } /* if select timed out */
+
+ /* select() didn't timeout, something happened */
+
+ /* Is it a signal? */
+ /* note: udhcp_sp_read checks FD_ISSET before reading */
+ switch (udhcp_sp_read(&rfds)) {
+ case SIGUSR1:
+ perform_renew();
+ if (state == RENEW_REQUESTED)
+ goto case_RENEW_REQUESTED;
+ /* Start things over */
+ packet_num = 0;
+ /* Kill any timeouts because the user wants this to hurry along */
+ timeout = 0;
+ continue;
+ case SIGUSR2:
+ perform_release(requested_ip, server_addr);
+ timeout = INT_MAX;
+ continue;
+ case SIGTERM:
+ bb_info_msg("Received SIGTERM");
+ if (opt & OPT_R) /* release on quit */
+ perform_release(requested_ip, server_addr);
+ goto ret0;
+ }
+
+ /* Is it a packet? */
+ if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds))
+ continue; /* no */
+
+ {
+ int len;
+
+ /* A packet is ready, read it */
if (listen_mode == LISTEN_KERNEL)
- len = get_packet(&packet, fd);
- else len = get_raw_packet(&packet, fd);
-
- if (len == -1 && errno != EINTR) {
- DEBUG(LOG_INFO, "error on read, %m, reopening socket");
- change_mode(listen_mode); /* just close and reopen */
- }
- if (len < 0) continue;
-
- if (packet.xid != xid) {
- DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
- (unsigned long) packet.xid, xid);
- continue;
+ len = udhcp_recv_kernel_packet(&packet, sockfd);
+ else
+ len = udhcp_recv_raw_packet(&packet, sockfd);
+ if (len == -1) {
+ /* Error is severe, reopen socket */
+ bb_info_msg("Read error: %s, reopening socket", strerror(errno));
+ sleep(discover_timeout); /* 3 seconds by default */
+ change_listen_mode(listen_mode); /* just close and reopen */
}
-
- if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
- DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
+ /* If this packet will turn out to be unrelated/bogus,
+ * we will go back and wait for next one.
+ * Be sure timeout is properly decreased. */
+ already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
+ if (len < 0)
continue;
+ }
+
+ if (packet.xid != xid) {
+ DEBUG("xid %x (our is %x), ignoring packet",
+ (unsigned)packet.xid, (unsigned)xid);
+ continue;
+ }
+
+ /* Ignore packets that aren't for us */
+ if (memcmp(packet.chaddr, client_config.arp, 6)) {
+ DEBUG("Packet does not have our chaddr - ignoring");
+ continue;
+ }
+
+ message = get_option(&packet, DHCP_MESSAGE_TYPE);
+ if (message == NULL) {
+ bb_error_msg("no message type option, ignoring packet");
+ continue;
+ }
+
+ switch (state) {
+ case INIT_SELECTING:
+ /* Must be a DHCPOFFER to one of our xid's */
+ if (*message == DHCPOFFER) {
+ /* TODO: why we don't just fetch server's IP from IP header? */
+ temp = get_option(&packet, DHCP_SERVER_ID);
+ if (!temp) {
+ bb_error_msg("no server ID in message");
+ continue;
+ /* still selecting - this server looks bad */
+ }
+ /* it IS unaligned sometimes, don't "optimize" */
+ move_from_unaligned32(server_addr, temp);
+ xid = packet.xid;
+ requested_ip = packet.yiaddr;
+
+ /* enter requesting state */
+ state = REQUESTING;
+ timeout = 0;
+ packet_num = 0;
+ already_waited_sec = 0;
}
-
- switch (state) {
- case INIT_SELECTING:
- /* Must be a DHCPOFFER to one of our xid's */
- if (*message == DHCPOFFER) {
- if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
- memcpy(&server_addr, temp, 4);
- xid = packet.xid;
- requested_ip = packet.yiaddr;
-
- /* enter requesting state */
- state = REQUESTING;
- timeout = now;
+ continue;
+ case REQUESTING:
+ case RENEWING:
+ case RENEW_REQUESTED:
+ case REBINDING:
+ if (*message == DHCPACK) {
+ temp = get_option(&packet, DHCP_LEASE_TIME);
+ if (!temp) {
+ bb_error_msg("no lease time with ACK, using 1 hour lease");
+ lease_seconds = 60 * 60;
+ } else {
+ /* it IS unaligned sometimes, don't "optimize" */
+ move_from_unaligned32(lease_seconds, temp);
+ lease_seconds = ntohl(lease_seconds);
+ lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */
+ if (lease_seconds < 10) /* and not too small */
+ lease_seconds = 10;
+ }
+#if ENABLE_FEATURE_UDHCPC_ARPING
+ if (opt & OPT_a) {
+/* RFC 2131 3.1 paragraph 5:
+ * "The client receives the DHCPACK message with configuration
+ * parameters. The client SHOULD perform a final check on the
+ * parameters (e.g., ARP for allocated network address), and notes
+ * the duration of the lease specified in the DHCPACK message. At this
+ * point, the client is configured. If the client detects that the
+ * address is already in use (e.g., through the use of ARP),
+ * the client MUST send a DHCPDECLINE message to the server and restarts
+ * the configuration process..." */
+ if (!arpping(packet.yiaddr,
+ (uint32_t) 0,
+ client_config.arp,
+ client_config.interface)
+ ) {
+ bb_info_msg("Offered address is in use "
+ "(got ARP reply), declining");
+ send_decline(xid, server_addr, packet.yiaddr);
+
+ if (state != REQUESTING)
+ udhcp_run_script(NULL, "deconfig");
+ change_listen_mode(LISTEN_RAW);
+ state = INIT_SELECTING;
+ requested_ip = 0;
+ timeout = tryagain_timeout;
packet_num = 0;
- } else {
- DEBUG(LOG_ERR, "No server ID in message");
+ already_waited_sec = 0;
+ continue; /* back to main loop */
}
}
- break;
- case RENEW_REQUESTED:
- case REQUESTING:
- case RENEWING:
- case REBINDING:
- if (*message == DHCPACK) {
- if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
- LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
- lease = 60 * 60;
- } else {
- memcpy(&lease, temp, 4);
- lease = ntohl(lease);
- }
-
- /* enter bound state */
- t1 = lease / 2;
-
- /* little fixed point for n * .875 */
- t2 = (lease * 0x7) >> 3;
+#endif
+ /* enter bound state */
+ timeout = lease_seconds / 2;
+ {
+ struct in_addr temp_addr;
temp_addr.s_addr = packet.yiaddr;
- LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
- inet_ntoa(temp_addr), lease);
- start = now;
- timeout = t1 + start;
- requested_ip = packet.yiaddr;
- run_script(&packet,
- ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
-
- state = BOUND;
- change_mode(LISTEN_NONE);
- if (client_config.quit_after_lease)
- return(0);
- if (!client_config.foreground)
- client_background();
-
- } else if (*message == DHCPNAK) {
- /* return to init state */
- LOG(LOG_INFO, "Received DHCP NAK");
- run_script(&packet, "nak");
- if (state != REQUESTING)
- run_script(NULL, "deconfig");
- state = INIT_SELECTING;
- timeout = now;
- requested_ip = 0;
- packet_num = 0;
- change_mode(LISTEN_RAW);
- sleep(3); /* avoid excessive network traffic */
+ bb_info_msg("Lease of %s obtained, lease time %u",
+ inet_ntoa(temp_addr), (unsigned)lease_seconds);
}
- break;
- /* case BOUND, RELEASED: - ignore all packets */
- }
- } else if (retval > 0 && FD_ISSET(udhcp_signal_pipe[0], &rfds)) {
- if (read(udhcp_signal_pipe[0], &sig, sizeof(sig)) < 0) {
- DEBUG(LOG_ERR, "Could not read signal: %m");
- continue; /* probably just EINTR */
+ requested_ip = packet.yiaddr;
+ udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");
+
+ state = BOUND;
+ change_listen_mode(LISTEN_NONE);
+ if (opt & OPT_q) { /* quit after lease */
+ if (opt & OPT_R) /* release on quit */
+ perform_release(requested_ip, server_addr);
+ goto ret0;
+ }
+ /* future renew failures should not exit (JM) */
+ opt &= ~OPT_n;
+#if BB_MMU /* NOMMU case backgrounded earlier */
+ if (!(opt & OPT_f)) {
+ client_background();
+ /* do not background again! */
+ opt = ((opt & ~OPT_b) | OPT_f);
+ }
+#endif
+ already_waited_sec = 0;
+ continue; /* back to main loop */
}
- switch (sig) {
- case SIGUSR1:
- perform_renew();
- break;
- case SIGUSR2:
- perform_release();
- break;
- case SIGTERM:
- LOG(LOG_INFO, "Received SIGTERM");
- return(0);
+ if (*message == DHCPNAK) {
+ /* return to init state */
+ bb_info_msg("Received DHCP NAK");
+ udhcp_run_script(&packet, "nak");
+ if (state != REQUESTING)
+ udhcp_run_script(NULL, "deconfig");
+ change_listen_mode(LISTEN_RAW);
+ sleep(3); /* avoid excessive network traffic */
+ state = INIT_SELECTING;
+ requested_ip = 0;
+ timeout = 0;
+ packet_num = 0;
+ already_waited_sec = 0;
}
- } else if (retval == -1 && errno == EINTR) {
- /* a signal was caught */
- } else {
- /* An error occured */
- DEBUG(LOG_ERR, "Error on select");
+ continue;
+ /* case BOUND: - ignore all packets */
+ /* case RELEASED: - ignore all packets */
}
-
- }
- return 0;
+ /* back to main loop */
+ } /* for (;;) - main loop ends */
+
+ ret0:
+ retval = 0;
+ ret:
+ /*if (client_config.pidfile) - remove_pidfile has its own check */
+ remove_pidfile(client_config.pidfile);
+ return retval;
}
diff --git a/release/src/router/busybox/networking/udhcp/dhcpc.h b/release/src/router/busybox/networking/udhcp/dhcpc.h
index e23d9d4f..7b779426 100644
--- a/release/src/router/busybox/networking/udhcp/dhcpc.h
+++ b/release/src/router/busybox/networking/udhcp/dhcpc.h
@@ -1,32 +1,51 @@
+/* vi: set sw=4 ts=4: */
/* dhcpc.h */
-#ifndef _DHCPC_H
-#define _DHCPC_H
-
-#define INIT_SELECTING 0
-#define REQUESTING 1
-#define BOUND 2
-#define RENEWING 3
-#define REBINDING 4
-#define INIT_REBOOT 5
-#define RENEW_REQUESTED 6
-#define RELEASED 7
+#ifndef UDHCP_DHCPC_H
+#define UDHCP_DHCPC_H 1
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
struct client_config_t {
- char foreground; /* Do not fork */
- char quit_after_lease; /* Quit after obtaining lease */
- char abort_if_no_lease; /* Abort if no lease */
- char background_if_no_lease; /* Fork to background if no lease */
- char *interface; /* The name of the interface to use */
- char *pidfile; /* Optionally store the process ID */
- char *script; /* User script to run at dhcp events */
- unsigned char *clientid; /* Optional client id to use */
- unsigned char *hostname; /* Optional hostname to use */
- int ifindex; /* Index number of the interface to use */
- unsigned char arp[6]; /* Our arp address */
+ uint8_t arp[6]; /* Our arp address */
+ /* TODO: combine flag fields into single "unsigned opt" */
+ /* (can be set directly to the result of getopt32) */
+ char no_default_options; /* Do not include default optins in request */
+ USE_FEATURE_UDHCP_PORT(uint16_t port;)
+ int ifindex; /* Index number of the interface to use */
+ uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */
+ const char *interface; /* The name of the interface to use */
+ char *pidfile; /* Optionally store the process ID */
+ const char *script; /* User script to run at dhcp events */
+ uint8_t *clientid; /* Optional client id to use */
+ uint8_t *vendorclass; /* Optional vendor class-id to use */
+ uint8_t *hostname; /* Optional hostname to use */
+ uint8_t *fqdn; /* Optional fully qualified domain name to use */
};
-extern struct client_config_t client_config;
+/* server_config sits in 1st half of bb_common_bufsiz1 */
+#define client_config (*(struct client_config_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE / 2]))
+
+#if ENABLE_FEATURE_UDHCP_PORT
+#define CLIENT_PORT (client_config.port)
+#else
+#define CLIENT_PORT 68
+#endif
+
+
+/*** clientpacket.h ***/
+
+uint32_t random_xid(void) FAST_FUNC;
+int send_discover(uint32_t xid, uint32_t requested) FAST_FUNC;
+int send_select(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC;
+#if ENABLE_FEATURE_UDHCPC_ARPING
+int send_decline(uint32_t xid, uint32_t server, uint32_t requested) FAST_FUNC;
+#endif
+int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC;
+int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) FAST_FUNC;
+int send_release(uint32_t server, uint32_t ciaddr) FAST_FUNC;
+
+int udhcp_recv_raw_packet(struct dhcpMessage *payload, int fd) FAST_FUNC;
+POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/release/src/router/busybox/networking/udhcp/dhcpd.c b/release/src/router/busybox/networking/udhcp/dhcpd.c
index c21cb72a..a82fd8c4 100644
--- a/release/src/router/busybox/networking/udhcp/dhcpd.c
+++ b/release/src/router/busybox/networking/udhcp/dhcpd.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/* dhcpd.c
*
* udhcp Server
@@ -6,248 +7,272 @@
*
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <time.h>
-#include <sys/time.h>
-
+#include <syslog.h>
+#include "common.h"
+#include "dhcpc.h"
#include "dhcpd.h"
-#include "arpping.h"
-#include "socket.h"
#include "options.h"
-#include "files.h"
-#include "serverpacket.h"
-#include "common.h"
/* globals */
struct dhcpOfferedAddr *leases;
-struct server_config_t server_config;
+/* struct server_config_t server_config is in bb_common_bufsiz1 */
-int udhcpd_main(int argc, char *argv[])
-{
+int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int udhcpd_main(int argc UNUSED_PARAM, char **argv)
+{
fd_set rfds;
- struct timeval tv;
- int server_socket = -1;
- int bytes, retval;
+ int server_socket = -1, retval, max_sock;
struct dhcpMessage packet;
- unsigned char *state;
- unsigned char *server_id, *requested;
- u_int32_t server_id_align, requested_align;
- unsigned long timeout_end;
+ uint8_t *state, *server_id, *requested;
+ uint32_t server_id_aligned = server_id_aligned; /* for compiler */
+ uint32_t requested_aligned = requested_aligned;
+ uint32_t static_lease_ip;
+ unsigned timeout_end;
+ unsigned num_ips;
+ unsigned opt;
struct option_set *option;
- struct dhcpOfferedAddr *lease;
- int max_sock;
- int sig;
- unsigned long num_ips;
-
- start_log("server");
-
- memset(&server_config, 0, sizeof(struct server_config_t));
-
- if (argc < 2)
- read_config(DHCPD_CONF_FILE);
- else read_config(argv[1]);
-
- if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
- memcpy(&server_config.lease, option->data + 2, 4);
+ struct dhcpOfferedAddr *lease, static_lease;
+ USE_FEATURE_UDHCP_PORT(char *str_P;)
+
+#if ENABLE_FEATURE_UDHCP_PORT
+ SERVER_PORT = 67;
+ CLIENT_PORT = 68;
+#endif
+
+ opt = getopt32(argv, "fS" USE_FEATURE_UDHCP_PORT("P:", &str_P));
+ argv += optind;
+ if (!(opt & 1)) { /* no -f */
+ bb_daemonize_or_rexec(0, argv);
+ logmode = LOGMODE_NONE;
+ }
+ if (opt & 2) { /* -S */
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode |= LOGMODE_SYSLOG;
+ }
+#if ENABLE_FEATURE_UDHCP_PORT
+ if (opt & 4) { /* -P */
+ SERVER_PORT = xatou16(str_P);
+ CLIENT_PORT = SERVER_PORT + 1;
+ }
+#endif
+ /* Would rather not do read_config before daemonization -
+ * otherwise NOMMU machines will parse config twice */
+ read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
+
+ /* Make sure fd 0,1,2 are open */
+ bb_sanitize_stdio();
+ /* Equivalent of doing a fflush after every \n */
+ setlinebuf(stdout);
+
+ /* Create pidfile */
+ write_pidfile(server_config.pidfile);
+ /* if (!..) bb_perror_msg("cannot create pidfile %s", pidfile); */
+
+ bb_info_msg("%s (v"BB_VER") started", applet_name);
+
+ option = find_option(server_config.options, DHCP_LEASE_TIME);
+ server_config.lease = LEASE_TIME;
+ if (option) {
+ move_from_unaligned32(server_config.lease, option->data + 2);
server_config.lease = ntohl(server_config.lease);
}
- else server_config.lease = LEASE_TIME;
-
+
/* Sanity check */
- num_ips = ntohl(server_config.end) - ntohl(server_config.start);
+ num_ips = server_config.end_ip - server_config.start_ip + 1;
if (server_config.max_leases > num_ips) {
- LOG(LOG_ERR, "max_leases value (%lu) not sane, "
- "setting to %lu instead",
- server_config.max_leases, num_ips);
+ bb_error_msg("max_leases=%u is too big, setting to %u",
+ (unsigned)server_config.max_leases, num_ips);
server_config.max_leases = num_ips;
}
- leases = xcalloc(sizeof(struct dhcpOfferedAddr), server_config.max_leases);
+ leases = xzalloc(server_config.max_leases * sizeof(*leases));
read_leases(server_config.lease_file);
- if (read_interface(server_config.interface, &server_config.ifindex,
- &server_config.server, server_config.arp) < 0)
- return(1);
-
-#ifndef CONFIG_FEATURE_UDHCP_DEBUG
- background(server_config.pidfile);
-#endif
+ if (udhcp_read_interface(server_config.interface, &server_config.ifindex,
+ &server_config.server, server_config.arp)) {
+ retval = 1;
+ goto ret;
+ }
- udhcp_set_signal_pipe(0);
+ /* Setup the signal pipe */
+ udhcp_sp_setup();
- timeout_end = time(0) + server_config.auto_time;
- while(1) { /* loop until universe collapses */
+ timeout_end = monotonic_sec() + server_config.auto_time;
+ while (1) { /* loop until universe collapses */
+ int bytes;
+ struct timeval tv;
- if (server_socket < 0)
- if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) {
- LOG(LOG_ERR, "FATAL: couldn't create server socket, %m");
- return(2);
- }
+ if (server_socket < 0) {
+ server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
+ server_config.interface);
+ }
- FD_ZERO(&rfds);
- FD_SET(server_socket, &rfds);
- FD_SET(udhcp_signal_pipe[0], &rfds);
+ max_sock = udhcp_sp_fd_set(&rfds, server_socket);
if (server_config.auto_time) {
- tv.tv_sec = timeout_end - time(0);
+ tv.tv_sec = timeout_end - monotonic_sec();
tv.tv_usec = 0;
}
+ retval = 0;
if (!server_config.auto_time || tv.tv_sec > 0) {
- max_sock = server_socket > udhcp_signal_pipe[0] ? server_socket : udhcp_signal_pipe[0];
- retval = select(max_sock + 1, &rfds, NULL, NULL,
+ retval = select(max_sock + 1, &rfds, NULL, NULL,
server_config.auto_time ? &tv : NULL);
- } else retval = 0; /* If we already timed out, fall through */
-
+ }
if (retval == 0) {
write_leases();
- timeout_end = time(0) + server_config.auto_time;
+ timeout_end = monotonic_sec() + server_config.auto_time;
continue;
- } else if (retval < 0 && errno != EINTR) {
- DEBUG(LOG_INFO, "error on select");
+ }
+ if (retval < 0 && errno != EINTR) {
+ DEBUG("error on select");
continue;
}
-
- if (FD_ISSET(udhcp_signal_pipe[0], &rfds)) {
- if (read(udhcp_signal_pipe[0], &sig, sizeof(sig)) < 0)
- continue; /* probably just EINTR */
- switch (sig) {
- case SIGUSR1:
- LOG(LOG_INFO, "Received a SIGUSR1");
- write_leases();
- /* why not just reset the timeout, eh */
- timeout_end = time(0) + server_config.auto_time;
- continue;
- case SIGTERM:
- LOG(LOG_INFO, "Received a SIGTERM");
- return(0);
- }
+
+ switch (udhcp_sp_read(&rfds)) {
+ case SIGUSR1:
+ bb_info_msg("Received a SIGUSR1");
+ write_leases();
+ /* why not just reset the timeout, eh */
+ timeout_end = monotonic_sec() + server_config.auto_time;
+ continue;
+ case SIGTERM:
+ bb_info_msg("Received a SIGTERM");
+ goto ret0;
+ case 0: /* no signal: read a packet */
+ break;
+ default: /* signal or error (probably EINTR): back to select */
+ continue;
}
- if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
+ bytes = udhcp_recv_kernel_packet(&packet, server_socket);
+ if (bytes < 0) {
+ /* bytes can also be -2 ("bad packet data") */
if (bytes == -1 && errno != EINTR) {
- DEBUG(LOG_INFO, "error on read, %m, reopening socket");
+ DEBUG("error on read, %s, reopening socket", strerror(errno));
close(server_socket);
server_socket = -1;
}
continue;
}
- if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
- DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
+ state = get_option(&packet, DHCP_MESSAGE_TYPE);
+ if (state == NULL) {
+ bb_error_msg("cannot get option from packet, ignoring");
continue;
}
-
- /* ADDME: look for a static lease */
- lease = find_lease_by_chaddr(packet.chaddr);
+
+ /* Look for a static lease */
+ static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
+ if (static_lease_ip) {
+ bb_info_msg("Found static lease: %x", static_lease_ip);
+
+ memcpy(&static_lease.chaddr, &packet.chaddr, 16);
+ static_lease.yiaddr = static_lease_ip;
+ static_lease.expires = 0;
+
+ lease = &static_lease;
+ } else {
+ lease = find_lease_by_chaddr(packet.chaddr);
+ }
+
switch (state[0]) {
case DHCPDISCOVER:
- DEBUG(LOG_INFO,"received DISCOVER");
-
- if (sendOffer(&packet) < 0) {
- LOG(LOG_ERR, "send OFFER failed");
+ DEBUG("Received DISCOVER");
+
+ if (send_offer(&packet) < 0) {
+ bb_error_msg("send OFFER failed");
}
- break;
- case DHCPREQUEST:
- DEBUG(LOG_INFO, "received REQUEST");
+ break;
+ case DHCPREQUEST:
+ DEBUG("received REQUEST");
requested = get_option(&packet, DHCP_REQUESTED_IP);
server_id = get_option(&packet, DHCP_SERVER_ID);
- if (requested) memcpy(&requested_align, requested, 4);
- if (server_id) memcpy(&server_id_align, server_id, 4);
-
- if (lease) { /*ADDME: or static lease */
+ if (requested)
+ move_from_unaligned32(requested_aligned, requested);
+ if (server_id)
+ move_from_unaligned32(server_id_aligned, server_id);
+
+ if (lease) {
if (server_id) {
/* SELECTING State */
- DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
- if (server_id_align == server_config.server && requested &&
- requested_align == lease->yiaddr) {
- sendACK(&packet, lease->yiaddr);
+ DEBUG("server_id = %08x", ntohl(server_id_aligned));
+ if (server_id_aligned == server_config.server
+ && requested
+ && requested_aligned == lease->yiaddr
+ ) {
+ send_ACK(&packet, lease->yiaddr);
}
- } else {
- if (requested) {
- /* INIT-REBOOT State */
- if (lease->yiaddr == requested_align)
- sendACK(&packet, lease->yiaddr);
- else sendNAK(&packet);
- } else {
- /* RENEWING or REBINDING State */
- if (lease->yiaddr == packet.ciaddr)
- sendACK(&packet, lease->yiaddr);
- else {
- /* don't know what to do!!!! */
- sendNAK(&packet);
- }
- }
+ } else if (requested) {
+ /* INIT-REBOOT State */
+ if (lease->yiaddr == requested_aligned)
+ send_ACK(&packet, lease->yiaddr);
+ else
+ send_NAK(&packet);
+ } else if (lease->yiaddr == packet.ciaddr) {
+ /* RENEWING or REBINDING State */
+ send_ACK(&packet, lease->yiaddr);
+ } else { /* don't know what to do!!!! */
+ send_NAK(&packet);
}
-
+
/* what to do if we have no record of the client */
} else if (server_id) {
/* SELECTING State */
} else if (requested) {
/* INIT-REBOOT State */
- if ((lease = find_lease_by_yiaddr(requested_align))) {
+ lease = find_lease_by_yiaddr(requested_aligned);
+ if (lease) {
if (lease_expired(lease)) {
/* probably best if we drop this lease */
memset(lease->chaddr, 0, 16);
/* make some contention for this address */
- } else sendNAK(&packet);
- } else if (requested_align < server_config.start ||
- requested_align > server_config.end) {
- sendNAK(&packet);
- } /* else remain silent */
+ } else
+ send_NAK(&packet);
+ } else {
+ uint32_t r = ntohl(requested_aligned);
+ if (r < server_config.start_ip
+ || r > server_config.end_ip
+ ) {
+ send_NAK(&packet);
+ }
+ /* else remain silent */
+ }
} else {
- /* RENEWING or REBINDING State */
+ /* RENEWING or REBINDING State */
}
break;
case DHCPDECLINE:
- DEBUG(LOG_INFO,"received DECLINE");
+ DEBUG("Received DECLINE");
if (lease) {
memset(lease->chaddr, 0, 16);
- lease->expires = time(0) + server_config.decline_time;
- }
+ lease->expires = time(NULL) + server_config.decline_time;
+ }
break;
case DHCPRELEASE:
- DEBUG(LOG_INFO,"received RELEASE");
- if (lease) lease->expires = time(0);
+ DEBUG("Received RELEASE");
+ if (lease)
+ lease->expires = time(NULL);
break;
case DHCPINFORM:
- DEBUG(LOG_INFO,"received INFORM");
+ DEBUG("Received INFORM");
send_inform(&packet);
- break;
+ break;
default:
- LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
+ bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
}
}
-
- return 0;
+ ret0:
+ retval = 0;
+ ret:
+ /*if (server_config.pidfile) - server_config.pidfile is never NULL */
+ remove_pidfile(server_config.pidfile);
+ return retval;
}
diff --git a/release/src/router/busybox/networking/udhcp/dhcpd.h b/release/src/router/busybox/networking/udhcp/dhcpd.h
index e219143c..9667c61e 100644
--- a/release/src/router/busybox/networking/udhcp/dhcpd.h
+++ b/release/src/router/busybox/networking/udhcp/dhcpd.h
@@ -1,11 +1,9 @@
+/* vi: set sw=4 ts=4: */
/* dhcpd.h */
-#ifndef _DHCPD_H
-#define _DHCPD_H
+#ifndef UDHCP_DHCPD_H
+#define UDHCP_DHCPD_H 1
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-
-#include "leases.h"
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
/************************************/
/* Defaults _you_ may want to tweak */
@@ -13,118 +11,126 @@
/* the period of time the client is allowed to use that address */
#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
+#define LEASES_FILE CONFIG_DHCPD_LEASES_FILE
/* where to find the DHCP server configuration file */
#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
-/*****************************************************************/
-/* Do not modify below here unless you know what you are doing!! */
-/*****************************************************************/
-
-/* DHCP protocol -- see RFC 2131 */
-#define SERVER_PORT 67
-#define CLIENT_PORT 68
-
-#define DHCP_MAGIC 0x63825363
-
-/* DHCP option codes (partial list) */
-#define DHCP_PADDING 0x00
-#define DHCP_SUBNET 0x01
-#define DHCP_TIME_OFFSET 0x02
-#define DHCP_ROUTER 0x03
-#define DHCP_TIME_SERVER 0x04
-#define DHCP_NAME_SERVER 0x05
-#define DHCP_DNS_SERVER 0x06
-#define DHCP_LOG_SERVER 0x07
-#define DHCP_COOKIE_SERVER 0x08
-#define DHCP_LPR_SERVER 0x09
-#define DHCP_HOST_NAME 0x0c
-#define DHCP_BOOT_SIZE 0x0d
-#define DHCP_DOMAIN_NAME 0x0f
-#define DHCP_SWAP_SERVER 0x10
-#define DHCP_ROOT_PATH 0x11
-#define DHCP_IP_TTL 0x17
-#define DHCP_MTU 0x1a
-#define DHCP_BROADCAST 0x1c
-#define DHCP_NTP_SERVER 0x2a
-#define DHCP_WINS_SERVER 0x2c
-#define DHCP_REQUESTED_IP 0x32
-#define DHCP_LEASE_TIME 0x33
-#define DHCP_OPTION_OVER 0x34
-#define DHCP_MESSAGE_TYPE 0x35
-#define DHCP_SERVER_ID 0x36
-#define DHCP_PARAM_REQ 0x37
-#define DHCP_MESSAGE 0x38
-#define DHCP_MAX_SIZE 0x39
-#define DHCP_T1 0x3a
-#define DHCP_T2 0x3b
-#define DHCP_VENDOR 0x3c
-#define DHCP_CLIENT_ID 0x3d
-
-#define DHCP_END 0xFF
-
-
-#define BOOTREQUEST 1
-#define BOOTREPLY 2
-
-#define ETH_10MB 1
-#define ETH_10MB_LEN 6
-
-#define DHCPDISCOVER 1
-#define DHCPOFFER 2
-#define DHCPREQUEST 3
-#define DHCPDECLINE 4
-#define DHCPACK 5
-#define DHCPNAK 6
-#define DHCPRELEASE 7
-#define DHCPINFORM 8
-
-#define BROADCAST_FLAG 0x8000
-
-#define OPTION_FIELD 0
-#define FILE_FIELD 1
-#define SNAME_FIELD 2
-
-/* miscellaneous defines */
-#define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff"
-#define OPT_CODE 0
-#define OPT_LEN 1
-#define OPT_DATA 2
-
struct option_set {
- unsigned char *data;
+ uint8_t *data;
struct option_set *next;
};
+struct static_lease {
+ struct static_lease *next;
+ uint32_t ip;
+ uint8_t mac[6];
+};
+
struct server_config_t {
- u_int32_t server; /* Our IP, in network order */
- u_int32_t start; /* Start address of leases, network order */
- u_int32_t end; /* End of leases, network order */
- struct option_set *options; /* List of DHCP options loaded from the config file */
- char *interface; /* The name of the interface to use */
- int ifindex; /* Index number of the interface to use */
- unsigned char arp[6]; /* Our arp address */
- unsigned long lease; /* lease time in seconds (host order) */
- unsigned long max_leases; /* maximum number of leases (including reserved address) */
- char remaining; /* should the lease file be interpreted as lease time remaining, or
- * as the time the lease expires */
- unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
- * if this is zero, it will only write one on SIGUSR1 */
- unsigned long decline_time; /* how long an address is reserved if a client returns a
- * decline message */
- unsigned long conflict_time; /* how long an arp conflict offender is leased for */
- unsigned long offer_time; /* how long an offered address is reserved */
- unsigned long min_lease; /* minimum lease a client can request*/
+ uint32_t server; /* Our IP, in network order */
+#if ENABLE_FEATURE_UDHCP_PORT
+ uint16_t port;
+#endif
+ /* start,end are in host order: we need to compare start <= ip <= end */
+ uint32_t start_ip; /* Start address of leases, in host order */
+ uint32_t end_ip; /* End of leases, in host order */
+ struct option_set *options; /* List of DHCP options loaded from the config file */
+ char *interface; /* The name of the interface to use */
+ int ifindex; /* Index number of the interface to use */
+ uint8_t arp[6]; /* Our arp address */
+// disabled: dumpleases has no way of knowing this value,
+// and will break if it's off. Now it's on always.
+// char remaining; /* Should the lease time in lease file
+// * be written as lease time remaining, or
+// * as the absolute time the lease expires */
+ uint32_t lease; /* lease time in seconds (host order) */
+ uint32_t max_leases; /* maximum number of leases (including reserved address) */
+ uint32_t auto_time; /* how long should udhcpd wait before writing a config file.
+ * if this is zero, it will only write one on SIGUSR1 */
+ uint32_t decline_time; /* how long an address is reserved if a client returns a
+ * decline message */
+ uint32_t conflict_time; /* how long an arp conflict offender is leased for */
+ uint32_t offer_time; /* how long an offered address is reserved */
+ uint32_t min_lease; /* minimum lease time a client can request */
+ uint32_t siaddr; /* next server bootp option */
char *lease_file;
char *pidfile;
- char *notify_file; /* What to run whenever leases are written */
- u_int32_t siaddr; /* next server bootp option */
- char *sname; /* bootp server name */
- char *boot_file; /* bootp boot file option */
-};
+ char *notify_file; /* What to run whenever leases are written */
+ char *sname; /* bootp server name */
+ char *boot_file; /* bootp boot file option */
+ struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
+};
+
+#define server_config (*(struct server_config_t*)&bb_common_bufsiz1)
+/* client_config sits in 2nd half of bb_common_bufsiz1 */
+
+#if ENABLE_FEATURE_UDHCP_PORT
+#define SERVER_PORT (server_config.port)
+#else
+#define SERVER_PORT 67
+#endif
+
+
+/*** leases.h ***/
+
+typedef uint32_t leasetime_t;
+typedef int32_t signed_leasetime_t;
+
+struct dhcpOfferedAddr {
+ uint8_t chaddr[16];
+ /* In network order */
+ uint32_t yiaddr;
+ /* Unix time when lease expires, regardless of value of
+ * server_config.remaining. Kept in memory in host order.
+ * When written to file, converted to network order
+ * and optionally adjusted (current time subtracted)
+ * if server_config.remaining = 1 */
+ leasetime_t expires;
+ uint8_t hostname[20]; /* (size is a multiply of 4) */
+};
-extern struct server_config_t server_config;
extern struct dhcpOfferedAddr *leases;
-
+
+struct dhcpOfferedAddr *add_lease(
+ const uint8_t *chaddr, uint32_t yiaddr,
+ leasetime_t leasetime, uint8_t *hostname
+ ) FAST_FUNC;
+int lease_expired(struct dhcpOfferedAddr *lease) FAST_FUNC;
+struct dhcpOfferedAddr *find_lease_by_chaddr(const uint8_t *chaddr) FAST_FUNC;
+struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr) FAST_FUNC;
+uint32_t find_free_or_expired_address(void) FAST_FUNC;
+
+
+/*** static_leases.h ***/
+
+/* Config file will pass static lease info to this function which will add it
+ * to a data structure that can be searched later */
+void addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t ip) FAST_FUNC;
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg) FAST_FUNC;
+/* Check to see if an ip is reserved as a static ip */
+int reservedIp(struct static_lease *lease_struct, uint32_t ip) FAST_FUNC;
+/* Print out static leases just to check what's going on (debug code) */
+void printStaticLeases(struct static_lease **lease_struct) FAST_FUNC;
+
+
+/*** serverpacket.h ***/
+
+int send_offer(struct dhcpMessage *oldpacket) FAST_FUNC;
+int send_NAK(struct dhcpMessage *oldpacket) FAST_FUNC;
+int send_ACK(struct dhcpMessage *oldpacket, uint32_t yiaddr) FAST_FUNC;
+int send_inform(struct dhcpMessage *oldpacket) FAST_FUNC;
+
+
+/*** files.h ***/
+
+void read_config(const char *file) FAST_FUNC;
+void write_leases(void) FAST_FUNC;
+void read_leases(const char *file) FAST_FUNC;
+struct option_set *find_option(struct option_set *opt_list, uint8_t code) FAST_FUNC;
+
+
+POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/release/src/router/busybox/networking/udhcp/dhcprelay.c b/release/src/router/busybox/networking/udhcp/dhcprelay.c
new file mode 100644
index 00000000..53540d1a
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/dhcprelay.c
@@ -0,0 +1,325 @@
+/* vi: set sw=4 ts=4: */
+/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
+ *
+ * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ *
+ * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
+ * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
+ * Zuercher Hochschule Winterthur,
+ * Netbeat AG
+ * Upstream has GPL v2 or later
+ */
+
+#include "common.h"
+#include "options.h"
+
+#define SERVER_PORT 67
+#define SELECT_TIMEOUT 5 /* select timeout in sec. */
+#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */
+
+/* This list holds information about clients. The xid_* functions manipulate this list. */
+struct xid_item {
+ unsigned timestamp;
+ int client;
+ uint32_t xid;
+ struct sockaddr_in ip;
+ struct xid_item *next;
+};
+
+#define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
+
+static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
+{
+ struct xid_item *item;
+
+ /* create new xid entry */
+ item = xmalloc(sizeof(struct xid_item));
+
+ /* add xid entry */
+ item->ip = *ip;
+ item->xid = xid;
+ item->client = client;
+ item->timestamp = monotonic_sec();
+ item->next = dhcprelay_xid_list.next;
+ dhcprelay_xid_list.next = item;
+
+ return item;
+}
+
+static void xid_expire(void)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ struct xid_item *last = &dhcprelay_xid_list;
+ unsigned current_time = monotonic_sec();
+
+ while (item != NULL) {
+ if ((current_time - item->timestamp) > MAX_LIFETIME) {
+ last->next = item->next;
+ free(item);
+ item = last->next;
+ } else {
+ last = item;
+ item = item->next;
+ }
+ }
+}
+
+static struct xid_item *xid_find(uint32_t xid)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ while (item != NULL) {
+ if (item->xid == xid) {
+ return item;
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+static void xid_del(uint32_t xid)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ struct xid_item *last = &dhcprelay_xid_list;
+ while (item != NULL) {
+ if (item->xid == xid) {
+ last->next = item->next;
+ free(item);
+ item = last->next;
+ } else {
+ last = item;
+ item = item->next;
+ }
+ }
+}
+
+/**
+ * get_dhcp_packet_type - gets the message type of a dhcp packet
+ * p - pointer to the dhcp packet
+ * returns the message type on success, -1 otherwise
+ */
+static int get_dhcp_packet_type(struct dhcpMessage *p)
+{
+ uint8_t *op;
+
+ /* it must be either a BOOTREQUEST or a BOOTREPLY */
+ if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
+ return -1;
+ /* get message type option */
+ op = get_option(p, DHCP_MESSAGE_TYPE);
+ if (op != NULL)
+ return op[0];
+ return -1;
+}
+
+/**
+ * get_client_devices - parses the devices list
+ * dev_list - comma separated list of devices
+ * returns array
+ */
+static char **get_client_devices(char *dev_list, int *client_number)
+{
+ char *s, **client_dev;
+ int i, cn;
+
+ /* copy list */
+ dev_list = xstrdup(dev_list);
+
+ /* get number of items, replace ',' with NULs */
+ s = dev_list;
+ cn = 1;
+ while (*s) {
+ if (*s == ',') {
+ *s = '\0';
+ cn++;
+ }
+ s++;
+ }
+ *client_number = cn;
+
+ /* create vector of pointers */
+ client_dev = xzalloc(cn * sizeof(*client_dev));
+ client_dev[0] = dev_list;
+ i = 1;
+ while (i != cn) {
+ client_dev[i] = client_dev[i - 1] + strlen(client_dev[i - 1]) + 1;
+ i++;
+ }
+ return client_dev;
+}
+
+
+/* Creates listen sockets (in fds) bound to client and server ifaces,
+ * and returns numerically max fd.
+ */
+static int init_sockets(char **client_ifaces, int num_clients,
+ char *server_iface, int *fds)
+{
+ int i, n;
+
+ /* talk to real server on bootps */
+ fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_iface);
+ n = fds[0];
+
+ for (i = 1; i < num_clients; i++) {
+ /* listen for clients on bootps */
+ fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client_ifaces[i-1]);
+ if (fds[i] > n)
+ n = fds[i];
+ }
+ return n;
+}
+
+
+/**
+ * pass_to_server() - forwards dhcp packets from client to server
+ * p - packet to send
+ * client - number of the client
+ */
+static void pass_to_server(struct dhcpMessage *p, int packet_len, int client, int *fds,
+ struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
+{
+ int res, type;
+ struct xid_item *item;
+
+ /* check packet_type */
+ type = get_dhcp_packet_type(p);
+ if (type != DHCPDISCOVER && type != DHCPREQUEST
+ && type != DHCPDECLINE && type != DHCPRELEASE
+ && type != DHCPINFORM
+ ) {
+ return;
+ }
+
+ /* create new xid entry */
+ item = xid_add(p->xid, client_addr, client);
+
+ /* forward request to LAN (server) */
+ errno = 0;
+ res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
+ sizeof(struct sockaddr_in));
+ if (res != packet_len) {
+ bb_perror_msg("sendto");
+ }
+}
+
+/**
+ * pass_to_client() - forwards dhcp packets from server to client
+ * p - packet to send
+ */
+static void pass_to_client(struct dhcpMessage *p, int packet_len, int *fds)
+{
+ int res, type;
+ struct xid_item *item;
+
+ /* check xid */
+ item = xid_find(p->xid);
+ if (!item) {
+ return;
+ }
+
+ /* check packet type */
+ type = get_dhcp_packet_type(p);
+ if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
+ return;
+ }
+
+ if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
+ item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ errno = 0;
+ res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*) &(item->ip),
+ sizeof(item->ip));
+ if (res != packet_len) {
+ bb_perror_msg("sendto");
+ return;
+ }
+
+ /* remove xid entry */
+ xid_del(p->xid);
+}
+
+int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dhcprelay_main(int argc, char **argv)
+{
+ struct dhcpMessage dhcp_msg;
+ struct sockaddr_in server_addr;
+ struct sockaddr_in client_addr;
+ fd_set rfds;
+ char **client_ifaces;
+ int *fds;
+ int num_sockets, max_socket;
+ uint32_t our_ip;
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(SERVER_PORT);
+
+ /* dhcprelay client_iface1,client_iface2,... server_iface [server_IP] */
+ if (argc == 4) {
+ if (!inet_aton(argv[3], &server_addr.sin_addr))
+ bb_perror_msg_and_die("bad server IP");
+ } else if (argc == 3) {
+ server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ } else {
+ bb_show_usage();
+ }
+
+ /* Produce list of client ifaces */
+ client_ifaces = get_client_devices(argv[1], &num_sockets);
+
+ num_sockets++; /* for server socket at fds[0] */
+ fds = xmalloc(num_sockets * sizeof(fds[0]));
+
+ /* Create sockets and bind one to every iface */
+ max_socket = init_sockets(client_ifaces, num_sockets, argv[2], fds);
+
+ /* Get our IP on server_iface */
+ if (udhcp_read_interface(argv[2], NULL, &our_ip, NULL))
+ return 1;
+
+ /* Main loop */
+ while (1) {
+//reinit stuff from time to time? go back to get_client_devices
+//every N minutes?
+ struct timeval tv;
+ size_t packlen;
+ socklen_t addr_size;
+ int i;
+
+ FD_ZERO(&rfds);
+ for (i = 0; i < num_sockets; i++)
+ FD_SET(fds[i], &rfds);
+ tv.tv_sec = SELECT_TIMEOUT;
+ tv.tv_usec = 0;
+ if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
+ /* server */
+ if (FD_ISSET(fds[0], &rfds)) {
+ packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
+ if (packlen > 0) {
+ pass_to_client(&dhcp_msg, packlen, fds);
+ }
+ }
+ /* clients */
+ for (i = 1; i < num_sockets; i++) {
+ if (!FD_ISSET(fds[i], &rfds))
+ continue;
+ addr_size = sizeof(struct sockaddr_in);
+ packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
+ (struct sockaddr *)(&client_addr), &addr_size);
+ if (packlen <= 0)
+ continue;
+
+ /* Get our IP on corresponding client_iface */
+//why? what if server can't route such IP?
+ if (udhcp_read_interface(client_ifaces[i-1], NULL, &dhcp_msg.giaddr, NULL)) {
+ /* Fall back to our server_iface's IP */
+//this makes more sense!
+ dhcp_msg.giaddr = our_ip;
+ }
+//maybe set dhcp_msg.flags |= BROADCAST_FLAG too?
+ pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
+ }
+ }
+ xid_expire();
+ } /* while (1) */
+
+ /* return 0; - not reached */
+}
diff --git a/release/src/router/busybox/networking/udhcp/domain_codec.c b/release/src/router/busybox/networking/udhcp/domain_codec.c
new file mode 100644
index 00000000..6f051c4b
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/domain_codec.c
@@ -0,0 +1,205 @@
+/* vi: set sw=4 ts=4: */
+
+/* RFC1035 domain compression routines (C) 2007 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Loosely based on the isc-dhcpd implementation by dhankins@isc.org
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_FEATURE_UDHCP_RFC3397
+
+#include "common.h"
+#include "options.h"
+
+#define NS_MAXDNAME 1025 /* max domain name length */
+#define NS_MAXCDNAME 255 /* max compressed domain name length */
+#define NS_MAXLABEL 63 /* max label length */
+#define NS_MAXDNSRCH 6 /* max domains in search path */
+#define NS_CMPRSFLGS 0xc0 /* name compression pointer flag */
+
+
+/* expand a RFC1035-compressed list of domain names "cstr", of length "clen";
+ * returns a newly allocated string containing the space-separated domains,
+ * prefixed with the contents of string pre, or NULL if an error occurs.
+ */
+char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
+{
+ const uint8_t *c;
+ int crtpos, retpos, depth, plen = 0, len = 0;
+ char *dst = NULL;
+
+ if (!cstr)
+ return NULL;
+
+ if (pre)
+ plen = strlen(pre);
+
+ /* We make two passes over the cstr string. First, we compute
+ * how long the resulting string would be. Then we allocate a
+ * new buffer of the required length, and fill it in with the
+ * expanded content. The advantage of this approach is not
+ * having to deal with requiring callers to supply their own
+ * buffer, then having to check if it's sufficiently large, etc.
+ */
+
+ while (!dst) {
+
+ if (len > 0) { /* second pass? allocate dst buffer and copy pre */
+ dst = xmalloc(len + plen);
+ memcpy(dst, pre, plen);
+ }
+
+ crtpos = retpos = depth = len = 0;
+
+ while (crtpos < clen) {
+ c = cstr + crtpos;
+
+ if ((*c & NS_CMPRSFLGS) != 0) { /* pointer */
+ if (crtpos + 2 > clen) /* no offset to jump to? abort */
+ return NULL;
+ if (retpos == 0) /* toplevel? save return spot */
+ retpos = crtpos + 2;
+ depth++;
+ crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */
+ } else if (*c) { /* label */
+ if (crtpos + *c + 1 > clen) /* label too long? abort */
+ return NULL;
+ if (dst)
+ memcpy(dst + plen + len, c + 1, *c);
+ len += *c + 1;
+ crtpos += *c + 1;
+ if (dst)
+ *(dst + plen + len - 1) = '.';
+ } else { /* null: end of current domain name */
+ if (retpos == 0) { /* toplevel? keep going */
+ crtpos++;
+ } else { /* return to toplevel saved spot */
+ crtpos = retpos;
+ retpos = depth = 0;
+ }
+ if (dst)
+ *(dst + plen + len - 1) = ' ';
+ }
+
+ if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */
+ len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */
+ return NULL;
+ }
+
+ if (!len) /* expanded string has 0 length? abort */
+ return NULL;
+
+ if (dst)
+ *(dst + plen + len - 1) = '\0';
+ }
+
+ return dst;
+}
+
+/* Convert a domain name (src) from human-readable "foo.blah.com" format into
+ * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or
+ * NULL if an error occurs.
+ */
+static uint8_t *convert_dname(const char *src)
+{
+ uint8_t c, *res, *lp, *rp;
+ int len;
+
+ res = xmalloc(strlen(src) + 2);
+ rp = lp = res;
+ rp++;
+
+ for (;;) {
+ c = (uint8_t)*src++;
+ if (c == '.' || c == '\0') { /* end of label */
+ len = rp - lp - 1;
+ /* label too long, too short, or two '.'s in a row? abort */
+ if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) {
+ free(res);
+ return NULL;
+ }
+ *lp = len;
+ lp = rp++;
+ if (c == '\0' || *src == '\0') /* end of dname */
+ break;
+ } else {
+ if (c >= 0x41 && c <= 0x5A) /* uppercase? convert to lower */
+ c += 0x20;
+ *rp++ = c;
+ }
+ }
+
+ *lp = 0;
+ if (rp - res > NS_MAXCDNAME) { /* dname too long? abort */
+ free(res);
+ return NULL;
+ }
+ return res;
+}
+
+/* returns the offset within cstr at which dname can be found, or -1
+ */
+static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname)
+{
+ const uint8_t *c, *d;
+ int off, inc;
+
+ /* find all labels in cstr */
+ off = 0;
+ while (off < clen) {
+ c = cstr + off;
+
+ if ((*c & NS_CMPRSFLGS) != 0) { /* pointer, skip */
+ off += 2;
+ } else if (*c) { /* label, try matching dname */
+ inc = *c + 1;
+ d = dname;
+ while (*c == *d && memcmp(c + 1, d + 1, *c) == 0) {
+ if (*c == 0) /* match, return offset */
+ return off;
+ d += *c + 1;
+ c += *c + 1;
+ if ((*c & NS_CMPRSFLGS) != 0) /* pointer, jump */
+ c = cstr + (((*c & 0x3f) << 8) | (*(c + 1) & 0xff));
+ }
+ off += inc;
+ } else { /* null, skip */
+ off++;
+ }
+ }
+
+ return -1;
+}
+
+/* computes string to be appended to cstr so that src would be added to
+ * the compression (best case, it's a 2-byte pointer to some offset within
+ * cstr; worst case, it's all of src, converted to rfc3011 format).
+ * The computed string is returned directly; its length is returned via retlen;
+ * NULL and 0, respectively, are returned if an error occurs.
+ */
+uint8_t* FAST_FUNC dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen)
+{
+ uint8_t *d, *dname;
+ int off;
+
+ dname = convert_dname(src);
+ if (dname == NULL) {
+ *retlen = 0;
+ return NULL;
+ }
+
+ for (d = dname; *d != 0; d += *d + 1) {
+ off = find_offset(cstr, clen, d);
+ if (off >= 0) { /* found a match, add pointer and terminate string */
+ *d++ = NS_CMPRSFLGS;
+ *d = off;
+ break;
+ }
+ }
+
+ *retlen = d - dname + 1;
+ return dname;
+}
+
+#endif /* ENABLE_FEATURE_UDHCP_RFC3397 */
diff --git a/release/src/router/busybox/networking/udhcp/dumpleases.c b/release/src/router/busybox/networking/udhcp/dumpleases.c
index 4c6107cf..1558f884 100644
--- a/release/src/router/busybox/networking/udhcp/dumpleases.c
+++ b/release/src/router/busybox/networking/udhcp/dumpleases.c
@@ -1,88 +1,93 @@
-#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <time.h>
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
-#include "leases.h"
-#include "busybox.h"
+#include "common.h"
+#include "dhcpd.h"
+
+#if BB_LITTLE_ENDIAN
+static inline uint64_t hton64(uint64_t v)
+{
+ return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
+}
+#else
+#define hton64(v) (v)
+#endif
+#define ntoh64(v) hton64(v)
-#define REMAINING 0
-#define ABSOLUTE 1
-int dumpleases_main(int argc, char *argv[])
+int dumpleases_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dumpleases_main(int argc UNUSED_PARAM, char **argv)
{
- FILE *fp;
- int i, c, mode = REMAINING;
- long expires;
- const char *file = leases_file;
+ int fd;
+ int i;
+ unsigned opt;
+ int64_t written_at, curr, expires_abs;
+ const char *file = LEASES_FILE;
struct dhcpOfferedAddr lease;
struct in_addr addr;
-
- static const struct option options[] = {
- {"absolute", 0, 0, 'a'},
- {"remaining", 0, 0, 'r'},
- {"file", 1, 0, 'f'},
- {0, 0, 0, 0}
+
+ enum {
+ OPT_a = 0x1, // -a
+ OPT_r = 0x2, // -r
+ OPT_f = 0x4, // -f
};
+#if ENABLE_GETOPT_LONG
+ static const char dumpleases_longopts[] ALIGN1 =
+ "absolute\0" No_argument "a"
+ "remaining\0" No_argument "r"
+ "file\0" Required_argument "f"
+ ;
- while (1) {
- int option_index = 0;
- c = getopt_long(argc, argv, "arf:", options, &option_index);
- if (c == -1) break;
-
- switch (c) {
- case 'a': mode = ABSOLUTE; break;
- case 'r': mode = REMAINING; break;
- case 'f':
- file = optarg;
- break;
- default:
- bb_show_usage();
- }
- }
-
- fp = bb_xfopen(file, "r");
+ applet_long_options = dumpleases_longopts;
+#endif
+ opt_complementary = "=0:a--r:r--a";
+ opt = getopt32(argv, "arf:", &file);
- printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at");
- /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */
- while (fread(&lease, sizeof(lease), 1, fp)) {
+ fd = xopen(file, O_RDONLY);
+ printf("Mac Address IP Address Host Name Expires %s\n", (opt & OPT_a) ? "at" : "in");
+ /* "00:00:00:00:00:00 255.255.255.255 ABCDEFGHIJKLMNOPQRS Wed Jun 30 21:49:08 1993" */
+ /* "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */
+
+ if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
+ return 0;
+ written_at = ntoh64(written_at);
+ curr = time(NULL);
+ if (curr < written_at)
+ written_at = curr; /* lease file from future! :) */
+
+ while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
+ const char *fmt = ":%02x" + 1;
for (i = 0; i < 6; i++) {
- printf("%02x", lease.chaddr[i]);
- if (i != 5) printf(":");
+ printf(fmt, lease.chaddr[i]);
+ fmt = ":%02x";
}
addr.s_addr = lease.yiaddr;
- printf(" %-15s", inet_ntoa(addr));
- expires = ntohl(lease.expires);
- printf(" ");
- if (mode == REMAINING) {
- if (!expires) printf("expired\n");
- else {
- if (expires > 60*60*24) {
- printf("%ld days, ", expires / (60*60*24));
- expires %= 60*60*24;
- }
- if (expires > 60*60) {
- printf("%ld hours, ", expires / (60*60));
- expires %= 60*60;
- }
- if (expires > 60) {
- printf("%ld minutes, ", expires / 60);
- expires %= 60;
- }
- printf("%ld seconds\n", expires);
- }
- } else printf("%s", ctime(&expires));
+ /* actually, 15+1 and 19+1, +1 is a space between columns */
+ /* lease.hostname is char[20] and is always NUL terminated */
+ printf(" %-16s%-20s", inet_ntoa(addr), lease.hostname);
+ expires_abs = ntohl(lease.expires) + written_at;
+ if (expires_abs <= curr) {
+ puts("expired");
+ continue;
+ }
+ if (!(opt & OPT_a)) { /* no -a */
+ unsigned d, h, m;
+ unsigned expires = expires_abs - curr;
+ d = expires / (24*60*60); expires %= (24*60*60);
+ h = expires / (60*60); expires %= (60*60);
+ m = expires / 60; expires %= 60;
+ if (d)
+ printf("%u days ", d);
+ printf("%02u:%02u:%02u\n", h, m, (unsigned)expires);
+ } else { /* -a */
+ time_t t = expires_abs;
+ fputs(ctime(&t), stdout);
+ }
}
- fclose(fp);
-
+ /* close(fd); */
+
return 0;
}
diff --git a/release/src/router/busybox/networking/udhcp/files.c b/release/src/router/busybox/networking/udhcp/files.c
index 3d0344b0..55f35978 100644
--- a/release/src/router/busybox/networking/udhcp/files.c
+++ b/release/src/router/busybox/networking/udhcp/files.c
@@ -1,287 +1,433 @@
-/*
+/* vi: set sw=4 ts=4: */
+/*
* files.c -- DHCP server file manipulation *
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
-
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#include <ctype.h>
+#include <netinet/ether.h>
+
+#include "common.h"
#include "dhcpd.h"
-#include "files.h"
#include "options.h"
-#include "common.h"
-/* on these functions, make sure you datatype matches */
+#if BB_LITTLE_ENDIAN
+static inline uint64_t hton64(uint64_t v)
+{
+ return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
+}
+#else
+#define hton64(v) (v)
+#endif
+#define ntoh64(v) hton64(v)
+
+
+/* on these functions, make sure your datatype matches */
static int read_ip(const char *line, void *arg)
{
- struct in_addr *addr = arg;
- struct hostent *host;
- int retval = 1;
-
- if (!inet_aton(line, addr)) {
- if ((host = gethostbyname(line)))
- addr->s_addr = *((unsigned long *) host->h_addr_list[0]);
- else retval = 0;
- }
- return retval;
+ len_and_sockaddr *lsa;
+
+ lsa = host_and_af2sockaddr(line, 0, AF_INET);
+ if (!lsa)
+ return 0;
+ *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr;
+ free(lsa);
+ return 1;
+}
+
+
+static int read_mac(const char *line, void *arg)
+{
+ return NULL == ether_aton_r(line, (struct ether_addr *)arg);
}
static int read_str(const char *line, void *arg)
{
char **dest = arg;
-
- if (*dest) free(*dest);
- *dest = strdup(line);
-
+
+ free(*dest);
+ *dest = xstrdup(line);
return 1;
}
static int read_u32(const char *line, void *arg)
{
- u_int32_t *dest = arg;
- char *endptr;
- *dest = strtoul(line, &endptr, 0);
- return endptr[0] == '\0';
+ *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
+ return errno == 0;
}
static int read_yn(const char *line, void *arg)
{
char *dest = arg;
- int retval = 1;
- if (!strcasecmp("yes", line))
+ if (!strcasecmp("yes", line)) {
*dest = 1;
- else if (!strcasecmp("no", line))
+ return 1;
+ }
+ if (!strcasecmp("no", line)) {
*dest = 0;
- else retval = 0;
-
- return retval;
+ return 1;
+ }
+ return 0;
+}
+
+
+/* find option 'code' in opt_list */
+struct option_set* FAST_FUNC find_option(struct option_set *opt_list, uint8_t code)
+{
+ while (opt_list && opt_list->data[OPT_CODE] < code)
+ opt_list = opt_list->next;
+
+ if (opt_list && opt_list->data[OPT_CODE] == code)
+ return opt_list;
+ return NULL;
+}
+
+
+/* add an option to the opt_list */
+static void attach_option(struct option_set **opt_list,
+ const struct dhcp_option *option, char *buffer, int length)
+{
+ struct option_set *existing, *new, **curr;
+
+ existing = find_option(*opt_list, option->code);
+ if (!existing) {
+ DEBUG("Attaching option %02x to list", option->code);
+
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+ /* reuse buffer and length for RFC1035-formatted string */
+ buffer = (char *)dname_enc(NULL, 0, buffer, &length);
+#endif
+
+ /* make a new option */
+ new = xmalloc(sizeof(*new));
+ new->data = xmalloc(length + 2);
+ new->data[OPT_CODE] = option->code;
+ new->data[OPT_LEN] = length;
+ memcpy(new->data + 2, buffer, length);
+
+ curr = opt_list;
+ while (*curr && (*curr)->data[OPT_CODE] < option->code)
+ curr = &(*curr)->next;
+
+ new->next = *curr;
+ *curr = new;
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+ free(buffer);
+#endif
+ return;
+ }
+
+ /* add it to an existing option */
+ DEBUG("Attaching option %02x to existing member of list", option->code);
+ if (option->flags & OPTION_LIST) {
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+ /* reuse buffer and length for RFC1035-formatted string */
+ buffer = (char *)dname_enc(existing->data + 2,
+ existing->data[OPT_LEN], buffer, &length);
+#endif
+ if (existing->data[OPT_LEN] + length <= 255) {
+ existing->data = xrealloc(existing->data,
+ existing->data[OPT_LEN] + length + 3);
+ if ((option->flags & TYPE_MASK) == OPTION_STRING) {
+ /* ' ' can bring us to 256 - bad */
+ if (existing->data[OPT_LEN] + length >= 255)
+ return;
+ /* add space separator between STRING options in a list */
+ existing->data[existing->data[OPT_LEN] + 2] = ' ';
+ existing->data[OPT_LEN]++;
+ }
+ memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
+ existing->data[OPT_LEN] += length;
+ } /* else, ignore the data, we could put this in a second option in the future */
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+ free(buffer);
+#endif
+ } /* else, ignore the new data */
}
-#define READ_CONFIG_BUF_SIZE 512 /* domainname may have 254 chars */
/* read a dhcp option and add it to opt_list */
static int read_opt(const char *const_line, void *arg)
{
- char line[READ_CONFIG_BUF_SIZE];
struct option_set **opt_list = arg;
char *opt, *val, *endptr;
- struct dhcp_option *option;
- int retval = 0, length;
- char buffer[256]; /* max opt length */
- u_int16_t result_u16;
- u_int32_t result_u32;
- void *valptr;
-
- if ((opt = strtok(strcpy(line, const_line), " \t="))) {
-
- for (option = options; option->code; option++)
- if (!strcasecmp(option->name, opt))
- break;
-
- if (option->code) do {
+ char *line;
+ const struct dhcp_option *option;
+ int retval, length, idx;
+ char buffer[8] ALIGNED(4);
+ uint16_t *result_u16 = (uint16_t *) buffer;
+ uint32_t *result_u32 = (uint32_t *) buffer;
+
+ /* Cheat, the only const line we'll actually get is "" */
+ line = (char *) const_line;
+ opt = strtok(line, " \t=");
+ if (!opt)
+ return 0;
+
+ idx = index_in_strings(dhcp_option_strings, opt); /* NB: was strcasecmp! */
+ if (idx < 0)
+ return 0;
+ option = &dhcp_options[idx];
+
+ retval = 0;
+ do {
val = strtok(NULL, ", \t");
- if(!val)
+ if (!val) break;
+ length = dhcp_option_lengths[option->flags & TYPE_MASK];
+ retval = 0;
+ opt = buffer; /* new meaning for variable opt */
+ switch (option->flags & TYPE_MASK) {
+ case OPTION_IP:
+ retval = read_ip(val, buffer);
break;
- length = option_lengths[option->flags & TYPE_MASK];
- valptr = NULL;
- switch (option->flags & TYPE_MASK) {
- case OPTION_IP:
- retval = read_ip(val, buffer);
- break;
- case OPTION_IP_PAIR:
- retval = read_ip(val, buffer);
- if (!(val = strtok(NULL, ", \t/-"))) retval = 0;
- if (retval) retval = read_ip(val, buffer + 4);
- break;
- case OPTION_STRING:
- length = strlen(val);
- if (length > 0) {
- if (length > 254) length = 254;
- endptr = buffer + length;
- endptr[0] = 0;
- valptr = val;
- }
- break;
- case OPTION_BOOLEAN:
- retval = read_yn(val, buffer);
- break;
- case OPTION_U8:
- buffer[0] = strtoul(val, &endptr, 0);
- valptr = buffer;
- break;
- case OPTION_U16:
- result_u16 = htons(strtoul(val, &endptr, 0));
- valptr = &result_u16;
- break;
- case OPTION_S16:
- result_u16 = htons(strtol(val, &endptr, 0));
- valptr = &result_u16;
- break;
- case OPTION_U32:
- result_u32 = htonl(strtoul(val, &endptr, 0));
- valptr = &result_u32;
- break;
- case OPTION_S32:
- result_u32 = htonl(strtol(val, &endptr, 0));
- valptr = &result_u32;
- break;
- default:
- retval = 0;
- break;
+ case OPTION_IP_PAIR:
+ retval = read_ip(val, buffer);
+ val = strtok(NULL, ", \t/-");
+ if (!val)
+ retval = 0;
+ if (retval)
+ retval = read_ip(val, buffer + 4);
+ break;
+ case OPTION_STRING:
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ case OPTION_STR1035:
+#endif
+ length = strlen(val);
+ if (length > 0) {
+ if (length > 254) length = 254;
+ opt = val;
+ retval = 1;
}
- if(valptr) {
- memcpy(buffer, valptr, length);
+ break;
+ case OPTION_BOOLEAN:
+ retval = read_yn(val, buffer);
+ break;
+ case OPTION_U8:
+ buffer[0] = strtoul(val, &endptr, 0);
+ retval = (endptr[0] == '\0');
+ break;
+ /* htonX are macros in older libc's, using temp var
+ * in code below for safety */
+ /* TODO: use bb_strtoX? */
+ case OPTION_U16: {
+ unsigned long tmp = strtoul(val, &endptr, 0);
+ *result_u16 = htons(tmp);
+ retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/);
+ break;
+ }
+ case OPTION_S16: {
+ long tmp = strtol(val, &endptr, 0);
+ *result_u16 = htons(tmp);
+ retval = (endptr[0] == '\0');
+ break;
+ }
+ case OPTION_U32: {
+ unsigned long tmp = strtoul(val, &endptr, 0);
+ *result_u32 = htonl(tmp);
retval = (endptr[0] == '\0');
+ break;
}
- if (retval)
- attach_option(opt_list, option, buffer, length);
- else
+ case OPTION_S32: {
+ long tmp = strtol(val, &endptr, 0);
+ *result_u32 = htonl(tmp);
+ retval = (endptr[0] == '\0');
break;
- } while (option->flags & OPTION_LIST);
- }
+ }
+ default:
+ break;
+ }
+ if (retval)
+ attach_option(opt_list, option, opt, length);
+ } while (retval && option->flags & OPTION_LIST);
return retval;
}
+static int read_staticlease(const char *const_line, void *arg)
+{
+ char *line;
+ char *mac_string;
+ char *ip_string;
+ struct ether_addr mac_bytes;
+ uint32_t ip;
-static const struct config_keyword keywords[] = {
- /* keyword handler variable address default */
- {"start", read_ip, &(server_config.start), "192.168.0.20"},
- {"end", read_ip, &(server_config.end), "192.168.0.254"},
- {"interface", read_str, &(server_config.interface), "eth0"},
- {"option", read_opt, &(server_config.options), ""},
- {"opt", read_opt, &(server_config.options), ""},
- {"max_leases", read_u32, &(server_config.max_leases), "254"},
- {"remaining", read_yn, &(server_config.remaining), "yes"},
- {"auto_time", read_u32, &(server_config.auto_time), "7200"},
- {"decline_time",read_u32, &(server_config.decline_time),"3600"},
- {"conflict_time",read_u32,&(server_config.conflict_time),"3600"},
- {"offer_time", read_u32, &(server_config.offer_time), "60"},
- {"min_lease", read_u32, &(server_config.min_lease), "60"},
- {"lease_file", read_str, &(server_config.lease_file), leases_file},
- {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
- {"notify_file", read_str, &(server_config.notify_file), ""},
- {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
- {"sname", read_str, &(server_config.sname), ""},
- {"boot_file", read_str, &(server_config.boot_file), ""},
- /*ADDME: static lease */
- {"", NULL, NULL, ""}
+ /* Read mac */
+ line = (char *) const_line;
+ mac_string = strtok_r(line, " \t", &line);
+ read_mac(mac_string, &mac_bytes);
+
+ /* Read ip */
+ ip_string = strtok_r(NULL, " \t", &line);
+ read_ip(ip_string, &ip);
+
+ addStaticLease(arg, (uint8_t*) &mac_bytes, ip);
+
+ if (ENABLE_UDHCP_DEBUG) printStaticLeases(arg);
+
+ return 1;
+}
+
+
+struct config_keyword {
+ const char *keyword;
+ int (*handler)(const char *line, void *var);
+ void *var;
+ const char *def;
};
+static const struct config_keyword keywords[] = {
+ /* keyword handler variable address default */
+ {"start", read_ip, &(server_config.start_ip), "192.168.0.20"},
+ {"end", read_ip, &(server_config.end_ip), "192.168.0.254"},
+ {"interface", read_str, &(server_config.interface), "eth0"},
+ /* Avoid "max_leases value not sane" warning by setting default
+ * to default_end_ip - default_start_ip + 1: */
+ {"max_leases", read_u32, &(server_config.max_leases), "235"},
+// {"remaining", read_yn, &(server_config.remaining), "yes"},
+ {"auto_time", read_u32, &(server_config.auto_time), "7200"},
+ {"decline_time", read_u32, &(server_config.decline_time), "3600"},
+ {"conflict_time",read_u32, &(server_config.conflict_time),"3600"},
+ {"offer_time", read_u32, &(server_config.offer_time), "60"},
+ {"min_lease", read_u32, &(server_config.min_lease), "60"},
+ {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE},
+ {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
+ {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
+ /* keywords with no defaults must be last! */
+ {"option", read_opt, &(server_config.options), ""},
+ {"opt", read_opt, &(server_config.options), ""},
+ {"notify_file", read_str, &(server_config.notify_file), ""},
+ {"sname", read_str, &(server_config.sname), ""},
+ {"boot_file", read_str, &(server_config.boot_file), ""},
+ {"static_lease", read_staticlease, &(server_config.static_leases), ""},
+};
+enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
-int read_config(const char *file)
+void FAST_FUNC read_config(const char *file)
{
- FILE *in;
- char buffer[READ_CONFIG_BUF_SIZE], orig[READ_CONFIG_BUF_SIZE];
- char *token, *line;
- int i;
+ parser_t *parser;
+ const struct config_keyword *k;
+ unsigned i;
+ char *token[2];
- for (i = 0; keywords[i].keyword[0]; i++)
- if (keywords[i].def[0])
- keywords[i].handler(keywords[i].def, keywords[i].var);
+ for (i = 0; i < KWS_WITH_DEFAULTS; i++)
+ keywords[i].handler(keywords[i].def, keywords[i].var);
- if (!(in = fopen(file, "r"))) {
- LOG(LOG_ERR, "unable to open config file: %s", file);
- return 0;
- }
-
- while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
- if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0';
- strcpy(orig, buffer);
- if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0';
- token = strtok(buffer, " \t");
- if(!token)
- continue;
- line = strtok(NULL, "");
- if(!line)
- continue;
- while(*line == '=' || isspace(*line))
- line++;
- /* eat trailing whitespace */
- for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--);
- line[i] = '\0';
- if (*line == '\0')
- continue;
-
- for (i = 0; keywords[i].keyword[0]; i++)
- if (!strcasecmp(token, keywords[i].keyword))
- if (!keywords[i].handler(line, keywords[i].var)) {
- LOG(LOG_ERR, "unable to parse '%s'", orig);
+ parser = config_open(file);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+ for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
+ if (!strcasecmp(token[0], k->keyword)) {
+ if (!k->handler(token[1], k->var)) {
+ bb_error_msg("can't parse line %u in %s",
+ parser->lineno, file);
/* reset back to the default value */
- keywords[i].handler(keywords[i].def, keywords[i].var);
+ k->handler(k->def, k->var);
}
+ break;
+ }
+ }
}
- fclose(in);
- return 1;
+ config_close(parser);
+
+ server_config.start_ip = ntohl(server_config.start_ip);
+ server_config.end_ip = ntohl(server_config.end_ip);
}
-void write_leases(void)
+void FAST_FUNC write_leases(void)
{
- FILE *fp;
- unsigned int i;
- char buf[255];
- time_t curr = time(0);
- unsigned long lease_time;
-
- if (!(fp = fopen(server_config.lease_file, "w"))) {
- LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file);
+ int fd;
+ unsigned i;
+ leasetime_t curr;
+ int64_t written_at;
+
+ fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
+ if (fd < 0)
return;
- }
-
+
+ curr = written_at = time(NULL);
+
+ written_at = hton64(written_at);
+ full_write(fd, &written_at, sizeof(written_at));
+
for (i = 0; i < server_config.max_leases; i++) {
- struct dhcpOfferedAddr lease;
- if (leases[i].yiaddr != 0) {
- if (server_config.remaining) {
- if (lease_expired(&(leases[i])))
- lease_time = 0;
- else lease_time = leases[i].expires - curr;
- } else lease_time = leases[i].expires;
- lease.expires = htonl(lease_time);
- memcpy(lease.chaddr, leases[i].chaddr, 16);
- lease.yiaddr = leases[i].yiaddr;
- fwrite(leases, sizeof(lease), 1, fp);
- }
+ leasetime_t tmp_time;
+
+ if (leases[i].yiaddr == 0)
+ continue;
+
+ /* Screw with the time in the struct, for easier writing */
+ tmp_time = leases[i].expires;
+
+ leases[i].expires -= curr;
+ if ((signed_leasetime_t) leases[i].expires < 0)
+ leases[i].expires = 0;
+ leases[i].expires = htonl(leases[i].expires);
+
+ /* No error check. If the file gets truncated,
+ * we lose some leases on restart. Oh well. */
+ full_write(fd, &leases[i], sizeof(leases[i]));
+
+ /* Then restore it when done */
+ leases[i].expires = tmp_time;
}
- fclose(fp);
-
+ close(fd);
+
if (server_config.notify_file) {
- sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file);
- system(buf);
+// TODO: vfork-based child creation
+ char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file);
+ system(cmd);
+ free(cmd);
}
}
-void read_leases(const char *file)
+void FAST_FUNC read_leases(const char *file)
{
- FILE *fp;
- unsigned int i = 0;
struct dhcpOfferedAddr lease;
-
- if (!(fp = fopen(file, "r"))) {
- LOG(LOG_ERR, "Unable to open %s for reading", file);
+ int64_t written_at, time_passed;
+ int fd;
+ USE_UDHCP_DEBUG(unsigned i;)
+
+ fd = open_or_warn(file, O_RDONLY);
+ if (fd < 0)
return;
- }
-
- while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) {
- /* ADDME: is it a static lease */
- if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
- lease.expires = ntohl(lease.expires);
- if (!server_config.remaining) lease.expires -= time(0);
- if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
- LOG(LOG_WARNING, "Too many leases while loading %s\n", file);
+
+ if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
+ goto ret;
+ written_at = ntoh64(written_at);
+
+ time_passed = time(NULL) - written_at;
+ /* Strange written_at, or lease file from old version of udhcpd
+ * which had no "written_at" field? */
+ if ((uint64_t)time_passed > 12 * 60 * 60)
+ goto ret;
+
+ USE_UDHCP_DEBUG(i = 0;)
+ while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
+ /* ADDME: what if it matches some static lease? */
+ uint32_t y = ntohl(lease.yiaddr);
+ if (y >= server_config.start_ip && y <= server_config.end_ip) {
+ signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
+ if (expires <= 0)
+ continue;
+ /* NB: add_lease takes "relative time", IOW,
+ * lease duration, not lease deadline. */
+ if (!(add_lease(lease.chaddr, lease.yiaddr, expires, NULL /* was lease.hostname. bug in add_lease, disabled */ ))) {
+ bb_error_msg("too many leases while loading %s", file);
break;
- }
- i++;
+ }
+ USE_UDHCP_DEBUG(i++;)
}
}
- DEBUG(LOG_INFO, "Read %d leases", i);
- fclose(fp);
+ DEBUG("Read %d leases", i);
+ ret:
+ close(fd);
}
diff --git a/release/src/router/busybox/networking/udhcp/files.h b/release/src/router/busybox/networking/udhcp/files.h
deleted file mode 100644
index 279738ad..00000000
--- a/release/src/router/busybox/networking/udhcp/files.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* files.h */
-#ifndef _FILES_H
-#define _FILES_H
-
-struct config_keyword {
- const char *keyword;
- int (* const handler)(const char *line, void *var);
- void *var;
- const char *def;
-};
-
-
-int read_config(const char *file);
-void write_leases(void);
-void read_leases(const char *file);
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/frontend.c b/release/src/router/busybox/networking/udhcp/frontend.c
deleted file mode 100644
index de577950..00000000
--- a/release/src/router/busybox/networking/udhcp/frontend.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <string.h>
-
-extern int udhcpd_main(int argc, char *argv[]);
-extern int udhcpc_main(int argc, char *argv[]);
-
-int main(int argc, char *argv[])
-{
- int ret = 0;
- char *base = strrchr(argv[0], '/');
-
- if (strstr(base ? (base + 1) : argv[0], "dhcpd"))
- ret = udhcpd_main(argc, argv);
- else ret = udhcpc_main(argc, argv);
-
- return ret;
-}
diff --git a/release/src/router/busybox/networking/udhcp/leases.c b/release/src/router/busybox/networking/udhcp/leases.c
index 0b6b409c..e17fb9e3 100644
--- a/release/src/router/busybox/networking/udhcp/leases.c
+++ b/release/src/router/busybox/networking/udhcp/leases.c
@@ -1,149 +1,181 @@
-/*
- * leases.c -- tools to manage DHCP leases
+/* vi: set sw=4 ts=4: */
+/*
+ * leases.c -- tools to manage DHCP leases
* Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
-#include <time.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include "dhcpd.h"
-#include "files.h"
-#include "options.h"
-#include "arpping.h"
#include "common.h"
+#include "dhcpd.h"
-unsigned char blank_chaddr[] = {[0 ... 15] = 0};
+/* Find the oldest expired lease, NULL if there are no expired leases */
+static struct dhcpOfferedAddr *oldest_expired_lease(void)
+{
+ struct dhcpOfferedAddr *oldest_lease = NULL;
+ leasetime_t oldest_time = time(NULL);
+ unsigned i;
+
+ /* Unexpired leases have leases[i].expires >= current time
+ * and therefore can't ever match */
+ for (i = 0; i < server_config.max_leases; i++) {
+ if (leases[i].expires < oldest_time) {
+ oldest_time = leases[i].expires;
+ oldest_lease = &(leases[i]);
+ }
+ }
+ return oldest_lease;
+}
+
-/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
-void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr)
+/* Clear every lease out that chaddr OR yiaddr matches and is nonzero */
+static void clear_lease(const uint8_t *chaddr, uint32_t yiaddr)
{
- unsigned int i, j;
-
- for (j = 0; j < 16 && !chaddr[j]; j++);
-
- for (i = 0; i < server_config.max_leases; i++)
- if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
- (yiaddr && leases[i].yiaddr == yiaddr)) {
- memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
+ unsigned i, j;
+
+ for (j = 0; j < 16 && !chaddr[j]; j++)
+ continue;
+
+ for (i = 0; i < server_config.max_leases; i++) {
+ if ((j != 16 && memcmp(leases[i].chaddr, chaddr, 16) == 0)
+ || (yiaddr && leases[i].yiaddr == yiaddr)
+ ) {
+ memset(&(leases[i]), 0, sizeof(leases[i]));
}
+ }
}
-/* add a lease into the table, clearing out any old ones */
-struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease)
+/* Add a lease into the table, clearing out any old ones */
+struct dhcpOfferedAddr* FAST_FUNC add_lease(
+ const uint8_t *chaddr, uint32_t yiaddr,
+ leasetime_t leasetime, uint8_t *hostname)
{
struct dhcpOfferedAddr *oldest;
-
+ uint8_t hostname_length;
+
/* clean out any old ones */
clear_lease(chaddr, yiaddr);
-
+
oldest = oldest_expired_lease();
-
+
if (oldest) {
+ oldest->hostname[0] = '\0';
+ if (hostname) {
+ /* option size byte, + 1 for NUL */
+ hostname_length = hostname[-1] + 1;
+ if (hostname_length > sizeof(oldest->hostname))
+ hostname_length = sizeof(oldest->hostname);
+ hostname = (uint8_t*) safe_strncpy((char*)oldest->hostname, (char*)hostname, hostname_length);
+ /* sanitization (s/non-ASCII/^/g) */
+ while (*hostname) {
+ if (*hostname < ' ' || *hostname > 126)
+ *hostname = '^';
+ hostname++;
+ }
+ }
memcpy(oldest->chaddr, chaddr, 16);
oldest->yiaddr = yiaddr;
- oldest->expires = time(0) + lease;
+ oldest->expires = time(NULL) + leasetime;
}
-
+
return oldest;
}
-/* true if a lease has expired */
-int lease_expired(struct dhcpOfferedAddr *lease)
+/* True if a lease has expired */
+int FAST_FUNC lease_expired(struct dhcpOfferedAddr *lease)
{
- return (lease->expires < (unsigned long) time(0));
-}
-
-
-/* Find the oldest expired lease, NULL if there are no expired leases */
-struct dhcpOfferedAddr *oldest_expired_lease(void)
-{
- struct dhcpOfferedAddr *oldest = NULL;
- unsigned long oldest_lease = time(0);
- unsigned int i;
-
-
- for (i = 0; i < server_config.max_leases; i++)
- if (oldest_lease > leases[i].expires) {
- oldest_lease = leases[i].expires;
- oldest = &(leases[i]);
- }
- return oldest;
-
+ return (lease->expires < (leasetime_t) time(NULL));
}
/* Find the first lease that matches chaddr, NULL if no match */
-struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr)
+struct dhcpOfferedAddr* FAST_FUNC find_lease_by_chaddr(const uint8_t *chaddr)
{
- unsigned int i;
+ unsigned i;
for (i = 0; i < server_config.max_leases; i++)
- if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
-
+ if (!memcmp(leases[i].chaddr, chaddr, 16))
+ return &(leases[i]);
+
return NULL;
}
/* Find the first lease that matches yiaddr, NULL is no match */
-struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr)
+struct dhcpOfferedAddr* FAST_FUNC find_lease_by_yiaddr(uint32_t yiaddr)
{
- unsigned int i;
+ unsigned i;
for (i = 0; i < server_config.max_leases; i++)
- if (leases[i].yiaddr == yiaddr) return &(leases[i]);
-
+ if (leases[i].yiaddr == yiaddr)
+ return &(leases[i]);
+
return NULL;
}
/* check is an IP is taken, if it is, add it to the lease table */
-static int check_ip(u_int32_t addr)
+static int nobody_responds_to_arp(uint32_t addr)
{
+ /* 16 zero bytes */
+ static const uint8_t blank_chaddr[16] = { 0 };
+ /* = { 0 } helps gcc to put it in rodata, not bss */
+
struct in_addr temp;
+ int r;
+
+ r = arpping(addr, server_config.server, server_config.arp, server_config.interface);
+ if (r)
+ return r;
- if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
- temp.s_addr = addr;
- LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds",
- inet_ntoa(temp), server_config.conflict_time);
- add_lease(blank_chaddr, addr, server_config.conflict_time);
- return 1;
- } else return 0;
+ temp.s_addr = addr;
+ bb_info_msg("%s belongs to someone, reserving it for %u seconds",
+ inet_ntoa(temp), (unsigned)server_config.conflict_time);
+ add_lease(blank_chaddr, addr, server_config.conflict_time, NULL);
+ return 0;
}
-/* find an assignable address, it check_expired is true, we check all the expired leases as well.
- * Maybe this should try expired leases by age... */
-u_int32_t find_address(int check_expired)
+
+/* Find a new usable (we think) address. */
+uint32_t FAST_FUNC find_free_or_expired_address(void)
{
- u_int32_t addr, ret;
- struct dhcpOfferedAddr *lease = NULL;
+ uint32_t addr;
+ struct dhcpOfferedAddr *oldest_lease = NULL;
- addr = ntohl(server_config.start); /* addr is in host order here */
- for (;addr <= ntohl(server_config.end); addr++) {
+ addr = server_config.start_ip; /* addr is in host order here */
+ for (; addr <= server_config.end_ip; addr++) {
+ uint32_t net_addr;
+ struct dhcpOfferedAddr *lease;
/* ie, 192.168.55.0 */
- if (!(addr & 0xFF)) continue;
-
+ if ((addr & 0xff) == 0)
+ continue;
/* ie, 192.168.55.255 */
- if ((addr & 0xFF) == 0xFF) continue;
-
- /* lease is not taken */
- ret = htonl(addr);
- if ((!(lease = find_lease_by_yiaddr(ret)) ||
-
- /* or it expired and we are checking for expired leases */
- (check_expired && lease_expired(lease))) &&
-
- /* and it isn't on the network */
- !check_ip(ret)) {
- return ret;
- break;
+ if ((addr & 0xff) == 0xff)
+ continue;
+ net_addr = htonl(addr);
+ /* addr has a static lease? */
+ if (reservedIp(server_config.static_leases, net_addr))
+ continue;
+
+ lease = find_lease_by_yiaddr(net_addr);
+ if (!lease) {
+ if (nobody_responds_to_arp(net_addr))
+ return net_addr;
+ } else {
+ if (!oldest_lease || lease->expires < oldest_lease->expires)
+ oldest_lease = lease;
}
}
+
+ if (oldest_lease && lease_expired(oldest_lease)
+ && nobody_responds_to_arp(oldest_lease->yiaddr)
+ ) {
+ return oldest_lease->yiaddr;
+ }
+
return 0;
}
diff --git a/release/src/router/busybox/networking/udhcp/leases.h b/release/src/router/busybox/networking/udhcp/leases.h
deleted file mode 100644
index d54cd11c..00000000
--- a/release/src/router/busybox/networking/udhcp/leases.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* leases.h */
-#ifndef _LEASES_H
-#define _LEASES_H
-
-
-struct dhcpOfferedAddr {
- u_int8_t chaddr[16];
- u_int32_t yiaddr; /* network order */
- u_int32_t expires; /* host order */
-};
-
-extern const char leases_file[];
-
-extern unsigned char blank_chaddr[];
-
-void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr);
-struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease);
-int lease_expired(struct dhcpOfferedAddr *lease);
-struct dhcpOfferedAddr *oldest_expired_lease(void);
-struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr);
-struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr);
-u_int32_t find_address(int check_expired);
-
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/leases_file.c b/release/src/router/busybox/networking/udhcp/leases_file.c
deleted file mode 100644
index 96e2f2d1..00000000
--- a/release/src/router/busybox/networking/udhcp/leases_file.c
+++ /dev/null
@@ -1 +0,0 @@
-const char leases_file[] = "/var/lib/misc/udhcpd.leases";
diff --git a/release/src/router/busybox/networking/udhcp/libbb_udhcp.h b/release/src/router/busybox/networking/udhcp/libbb_udhcp.h
deleted file mode 100644
index e0942980..00000000
--- a/release/src/router/busybox/networking/udhcp/libbb_udhcp.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* libbb_udhcp.h - busybox compatability wrapper */
-
-#ifndef _LIBBB_UDHCP_H
-#define _LIBBB_UDHCP_H
-
-#ifdef IN_BUSYBOX
-#include "libbb.h"
-
-#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
-#define SYSLOG
-#endif
-
-#ifdef CONFIG_FEATURE_UDHCP_DEBUG
-#define DEBUG
-#endif
-
-#define COMBINED_BINARY
-#include "version.h"
-
-#else /* ! BB_VER */
-
-#define TRUE 1
-#define FALSE 0
-
-#define xmalloc malloc
-
-#endif /* BB_VER */
-
-#endif /* _LIBBB_UDHCP_H */
diff --git a/release/src/router/busybox/networking/udhcp/options.c b/release/src/router/busybox/networking/udhcp/options.c
index a3edd0b8..143a1fd1 100644
--- a/release/src/router/busybox/networking/udhcp/options.c
+++ b/release/src/router/busybox/networking/udhcp/options.c
@@ -1,143 +1,211 @@
-/*
- * options.c -- DHCP server option packet tools
+/* vi: set sw=4 ts=4: */
+/*
+ * options.c -- DHCP server option packet tools
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
-
-#include <stdlib.h>
-#include <string.h>
+#include "common.h"
#include "dhcpd.h"
-#include "files.h"
#include "options.h"
-#include "common.h"
-/* supported options are easily added here */
-struct dhcp_option options[] = {
- /* name[10] flags code */
- {"subnet", OPTION_IP | OPTION_REQ, 0x01},
- {"timezone", OPTION_S32, 0x02},
- {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
- {"timesvr", OPTION_IP | OPTION_LIST, 0x04},
- {"namesvr", OPTION_IP | OPTION_LIST, 0x05},
- {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
- {"logsvr", OPTION_IP | OPTION_LIST, 0x07},
- {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
- {"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
- {"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
- {"bootsize", OPTION_U16, 0x0d},
- {"domain", OPTION_STRING | OPTION_REQ, 0x0f},
- {"swapsvr", OPTION_IP, 0x10},
- {"rootpath", OPTION_STRING, 0x11},
- {"ipttl", OPTION_U8, 0x17},
- {"mtu", OPTION_U16, 0x1a},
- {"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
- {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a},
- {"wins", OPTION_IP | OPTION_LIST, 0x2c},
- {"requestip", OPTION_IP, 0x32},
- {"lease", OPTION_U32, 0x33},
- {"dhcptype", OPTION_U8, 0x35},
- {"serverid", OPTION_IP, 0x36},
- {"message", OPTION_STRING, 0x38},
- {"tftp", OPTION_STRING, 0x42},
- {"bootfile", OPTION_STRING, 0x43},
- {"", 0x00, 0x00}
+/* Supported options are easily added here */
+const struct dhcp_option dhcp_options[] = {
+ /* flags code */
+ { OPTION_IP | OPTION_REQ, 0x01 }, /* DHCP_SUBNET */
+ { OPTION_S32 , 0x02 }, /* DHCP_TIME_OFFSET */
+ { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03 }, /* DHCP_ROUTER */
+ { OPTION_IP | OPTION_LIST , 0x04 }, /* DHCP_TIME_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x05 }, /* DHCP_NAME_SERVER */
+ { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06 }, /* DHCP_DNS_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x07 }, /* DHCP_LOG_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x08 }, /* DHCP_COOKIE_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x09 }, /* DHCP_LPR_SERVER */
+ { OPTION_STRING | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */
+ { OPTION_U16 , 0x0d }, /* DHCP_BOOT_SIZE */
+ { OPTION_STRING | OPTION_LIST | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */
+ { OPTION_IP , 0x10 }, /* DHCP_SWAP_SERVER */
+ { OPTION_STRING , 0x11 }, /* DHCP_ROOT_PATH */
+ { OPTION_U8 , 0x17 }, /* DHCP_IP_TTL */
+ { OPTION_U16 , 0x1a }, /* DHCP_MTU */
+ { OPTION_IP | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST */
+ { OPTION_STRING , 0x28 }, /* nisdomain */
+ { OPTION_IP | OPTION_LIST , 0x29 }, /* nissrv */
+ { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x2c }, /* DHCP_WINS_SERVER */
+ { OPTION_IP , 0x32 }, /* DHCP_REQUESTED_IP */
+ { OPTION_U32 , 0x33 }, /* DHCP_LEASE_TIME */
+ { OPTION_U8 , 0x35 }, /* dhcptype */
+ { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */
+ { OPTION_STRING , 0x38 }, /* DHCP_MESSAGE */
+ { OPTION_STRING , 0x3C }, /* DHCP_VENDOR */
+ { OPTION_STRING , 0x3D }, /* DHCP_CLIENT_ID */
+ { OPTION_STRING , 0x42 }, /* tftp */
+ { OPTION_STRING , 0x43 }, /* bootfile */
+ { OPTION_STRING , 0x4D }, /* userclass */
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ { OPTION_STR1035 | OPTION_LIST , 0x77 }, /* search */
+#endif
+ /* MSIE's "Web Proxy Autodiscovery Protocol" support */
+ { OPTION_STRING , 0xfc }, /* wpad */
+
+ /* Options below have no match in dhcp_option_strings[],
+ * are not passed to dhcpc scripts, and cannot be specified
+ * with "option XXX YYY" syntax in dhcpd config file. */
+
+ { OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */
+ { } /* zeroed terminating entry */
};
+/* Used for converting options from incoming packets to env variables
+ * for udhcpc stript */
+/* Must match dhcp_options[] order */
+const char dhcp_option_strings[] ALIGN1 =
+ "subnet" "\0" /* DHCP_SUBNET */
+ "timezone" "\0" /* DHCP_TIME_OFFSET */
+ "router" "\0" /* DHCP_ROUTER */
+ "timesrv" "\0" /* DHCP_TIME_SERVER */
+ "namesrv" "\0" /* DHCP_NAME_SERVER */
+ "dns" "\0" /* DHCP_DNS_SERVER */
+ "logsrv" "\0" /* DHCP_LOG_SERVER */
+ "cookiesrv" "\0" /* DHCP_COOKIE_SERVER */
+ "lprsrv" "\0" /* DHCP_LPR_SERVER */
+ "hostname" "\0" /* DHCP_HOST_NAME */
+ "bootsize" "\0" /* DHCP_BOOT_SIZE */
+ "domain" "\0" /* DHCP_DOMAIN_NAME */
+ "swapsrv" "\0" /* DHCP_SWAP_SERVER */
+ "rootpath" "\0" /* DHCP_ROOT_PATH */
+ "ipttl" "\0" /* DHCP_IP_TTL */
+ "mtu" "\0" /* DHCP_MTU */
+ "broadcast" "\0" /* DHCP_BROADCAST */
+ "nisdomain" "\0" /* */
+ "nissrv" "\0" /* */
+ "ntpsrv" "\0" /* DHCP_NTP_SERVER */
+ "wins" "\0" /* DHCP_WINS_SERVER */
+ "requestip" "\0" /* DHCP_REQUESTED_IP */
+ "lease" "\0" /* DHCP_LEASE_TIME */
+ "dhcptype" "\0" /* */
+ "serverid" "\0" /* DHCP_SERVER_ID */
+ "message" "\0" /* DHCP_MESSAGE */
+ "vendorclass" "\0" /* DHCP_VENDOR */
+ "clientid" "\0" /* DHCP_CLIENT_ID */
+ "tftp" "\0"
+ "bootfile" "\0"
+ "userclass" "\0"
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ "search" "\0"
+#endif
+ /* MSIE's "Web Proxy Autodiscovery Protocol" support */
+ "wpad" "\0"
+ ;
+
+
/* Lengths of the different option types */
-int option_lengths[] = {
- [OPTION_IP] = 4,
- [OPTION_IP_PAIR] = 8,
- [OPTION_BOOLEAN] = 1,
- [OPTION_STRING] = 1,
- [OPTION_U8] = 1,
- [OPTION_U16] = 2,
- [OPTION_S16] = 2,
- [OPTION_U32] = 4,
- [OPTION_S32] = 4
+const uint8_t dhcp_option_lengths[] ALIGN1 = {
+ [OPTION_IP] = 4,
+ [OPTION_IP_PAIR] = 8,
+ [OPTION_BOOLEAN] = 1,
+ [OPTION_STRING] = 1,
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ [OPTION_STR1035] = 1,
+#endif
+ [OPTION_U8] = 1,
+ [OPTION_U16] = 2,
+ [OPTION_S16] = 2,
+ [OPTION_U32] = 4,
+ [OPTION_S32] = 4
};
-/* get an option with bounds checking (warning, not aligned). */
-unsigned char *get_option(struct dhcpMessage *packet, int code)
+/* get an option with bounds checking (warning, result is not aligned). */
+uint8_t* FAST_FUNC get_option(struct dhcpMessage *packet, int code)
{
- int i, length;
- unsigned char *optionptr;
- int over = 0, done = 0, curr = OPTION_FIELD;
-
+ uint8_t *optionptr;
+ int len;
+ int rem;
+ int overload = 0;
+ enum {
+ FILE_FIELD101 = FILE_FIELD * 0x101,
+ SNAME_FIELD101 = SNAME_FIELD * 0x101,
+ };
+
+ /* option bytes: [code][len][data1][data2]..[dataLEN] */
optionptr = packet->options;
- i = 0;
- length = 308;
- while (!done) {
- if (i >= length) {
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
+ rem = sizeof(packet->options);
+ while (1) {
+ if (rem <= 0) {
+ bb_error_msg("bogus packet, malformed option field");
return NULL;
}
- if (optionptr[i + OPT_CODE] == code) {
- if (i + 1 + optionptr[i + OPT_LEN] >= length) {
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
- return NULL;
- }
- return optionptr + i + 2;
- }
- switch (optionptr[i + OPT_CODE]) {
- case DHCP_PADDING:
- i++;
- break;
- case DHCP_OPTION_OVER:
- if (i + 1 + optionptr[i + OPT_LEN] >= length) {
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
- return NULL;
- }
- over = optionptr[i + 3];
- i += optionptr[OPT_LEN] + 2;
- break;
- case DHCP_END:
- if (curr == OPTION_FIELD && over & FILE_FIELD) {
+ if (optionptr[OPT_CODE] == DHCP_PADDING) {
+ rem--;
+ optionptr++;
+ continue;
+ }
+ if (optionptr[OPT_CODE] == DHCP_END) {
+ if ((overload & FILE_FIELD101) == FILE_FIELD) {
+ /* can use packet->file, and didn't look at it yet */
+ overload |= FILE_FIELD101; /* "we looked at it" */
optionptr = packet->file;
- i = 0;
- length = 128;
- curr = FILE_FIELD;
- } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
+ rem = sizeof(packet->file);
+ continue;
+ }
+ if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
+ /* can use packet->sname, and didn't look at it yet */
+ overload |= SNAME_FIELD101; /* "we looked at it" */
optionptr = packet->sname;
- i = 0;
- length = 64;
- curr = SNAME_FIELD;
- } else done = 1;
- break;
- default:
- i += optionptr[OPT_LEN + i] + 2;
+ rem = sizeof(packet->sname);
+ continue;
+ }
+ return NULL;
+ }
+ len = 2 + optionptr[OPT_LEN];
+ rem -= len;
+ if (rem < 0)
+ continue; /* complain and return NULL */
+
+ if (optionptr[OPT_CODE] == code)
+ return optionptr + OPT_DATA;
+
+ if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
+ overload |= optionptr[OPT_DATA];
+ /* fall through */
}
+ optionptr += len;
}
return NULL;
}
/* return the position of the 'end' option (no bounds checking) */
-int end_option(unsigned char *optionptr)
+int FAST_FUNC end_option(uint8_t *optionptr)
{
int i = 0;
-
+
while (optionptr[i] != DHCP_END) {
- if (optionptr[i] == DHCP_PADDING) i++;
- else i += optionptr[i + OPT_LEN] + 2;
+ if (optionptr[i] != DHCP_PADDING)
+ i += optionptr[i + OPT_LEN] + 1;
+ i++;
}
return i;
}
-/* add an option string to the options (an option string contains an option code,
- * length, then data) */
-int add_option_string(unsigned char *optionptr, unsigned char *string)
+/* add an option string to the options */
+/* option bytes: [code][len][data1][data2]..[dataLEN] */
+int FAST_FUNC add_option_string(uint8_t *optionptr, uint8_t *string)
{
int end = end_option(optionptr);
-
+
/* end position + string length + option code/length + end option */
- if (end + string[OPT_LEN] + 2 + 1 >= 308) {
- LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
+ if (end + string[OPT_LEN] + 2 + 1 >= DHCP_OPTIONS_BUFSIZE) {
+ bb_error_msg("option 0x%02x did not fit into the packet",
+ string[OPT_CODE]);
return 0;
}
- DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
+ DEBUG("adding option 0x%02x", string[OPT_CODE]);
memcpy(optionptr + end, string, string[OPT_LEN] + 2);
optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
return string[OPT_LEN] + 2;
@@ -145,84 +213,25 @@ int add_option_string(unsigned char *optionptr, unsigned char *string)
/* add a one to four byte option to a packet */
-int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data)
+int FAST_FUNC add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
{
- char length = 0;
- int i;
- unsigned char option[2 + 4];
- unsigned char *u8;
- u_int16_t *u16;
- u_int32_t *u32;
- u_int32_t aligned;
- u8 = (unsigned char *) &aligned;
- u16 = (u_int16_t *) &aligned;
- u32 = &aligned;
-
- for (i = 0; options[i].code; i++)
- if (options[i].code == code) {
- length = option_lengths[options[i].flags & TYPE_MASK];
+ const struct dhcp_option *dh;
+
+ for (dh = dhcp_options; dh->code; dh++) {
+ if (dh->code == code) {
+ uint8_t option[6], len;
+
+ option[OPT_CODE] = code;
+ len = dhcp_option_lengths[dh->flags & TYPE_MASK];
+ option[OPT_LEN] = len;
+ if (BB_BIG_ENDIAN)
+ data <<= 8 * (4 - len);
+ /* Assignment is unaligned! */
+ move_to_unaligned32(&option[OPT_DATA], data);
+ return add_option_string(optionptr, option);
}
-
- if (!length) {
- DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
- return 0;
- }
-
- option[OPT_CODE] = code;
- option[OPT_LEN] = length;
-
- switch (length) {
- case 1: *u8 = data; break;
- case 2: *u16 = data; break;
- case 4: *u32 = data; break;
}
- memcpy(option + 2, &aligned, length);
- return add_option_string(optionptr, option);
-}
-
-/* find option 'code' in opt_list */
-struct option_set *find_option(struct option_set *opt_list, char code)
-{
- while (opt_list && opt_list->data[OPT_CODE] < code)
- opt_list = opt_list->next;
-
- if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
- else return NULL;
-}
-
-
-/* add an option to the opt_list */
-void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
-{
- struct option_set *existing, *new, **curr;
-
- /* add it to an existing option */
- if ((existing = find_option(*opt_list, option->code))) {
- DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
- if (option->flags & OPTION_LIST) {
- if (existing->data[OPT_LEN] + length <= 255) {
- existing->data = realloc(existing->data,
- existing->data[OPT_LEN] + length + 2);
- memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
- existing->data[OPT_LEN] += length;
- } /* else, ignore the data, we could put this in a second option in the future */
- } /* else, ignore the new data */
- } else {
- DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
-
- /* make a new option */
- new = xmalloc(sizeof(struct option_set));
- new->data = xmalloc(length + 2);
- new->data[OPT_CODE] = option->code;
- new->data[OPT_LEN] = length;
- memcpy(new->data + 2, buffer, length);
-
- curr = opt_list;
- while (*curr && (*curr)->data[OPT_CODE] < option->code)
- curr = &(*curr)->next;
-
- new->next = *curr;
- *curr = new;
- }
+ bb_error_msg("cannot add option 0x%02x", code);
+ return 0;
}
diff --git a/release/src/router/busybox/networking/udhcp/options.h b/release/src/router/busybox/networking/udhcp/options.h
index 1fded2ef..23370da6 100644
--- a/release/src/router/busybox/networking/udhcp/options.h
+++ b/release/src/router/busybox/networking/udhcp/options.h
@@ -1,15 +1,19 @@
+/* vi: set sw=4 ts=4: */
/* options.h */
-#ifndef _OPTIONS_H
-#define _OPTIONS_H
+#ifndef UDHCP_OPTIONS_H
+#define UDHCP_OPTIONS_H 1
-#include "packet.h"
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
-#define TYPE_MASK 0x0F
+#define TYPE_MASK 0x0F
enum {
- OPTION_IP=1,
+ OPTION_IP = 1,
OPTION_IP_PAIR,
OPTION_STRING,
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ OPTION_STR1035, /* RFC1035 compressed domain name list */
+#endif
OPTION_BOOLEAN,
OPTION_U8,
OPTION_U16,
@@ -18,23 +22,93 @@ enum {
OPTION_S32
};
-#define OPTION_REQ 0x10 /* have the client request this option */
-#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
+#define OPTION_REQ 0x10 /* have the client request this option */
+#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
+
+/*****************************************************************/
+/* Do not modify below here unless you know what you are doing!! */
+/*****************************************************************/
+
+/* DHCP protocol -- see RFC 2131 */
+#define DHCP_MAGIC 0x63825363
+
+/* DHCP option codes (partial list) */
+#define DHCP_PADDING 0x00
+#define DHCP_SUBNET 0x01
+#define DHCP_TIME_OFFSET 0x02
+#define DHCP_ROUTER 0x03
+#define DHCP_TIME_SERVER 0x04
+#define DHCP_NAME_SERVER 0x05
+#define DHCP_DNS_SERVER 0x06
+#define DHCP_LOG_SERVER 0x07
+#define DHCP_COOKIE_SERVER 0x08
+#define DHCP_LPR_SERVER 0x09
+#define DHCP_HOST_NAME 0x0c
+#define DHCP_BOOT_SIZE 0x0d
+#define DHCP_DOMAIN_NAME 0x0f
+#define DHCP_SWAP_SERVER 0x10
+#define DHCP_ROOT_PATH 0x11
+#define DHCP_IP_TTL 0x17
+#define DHCP_MTU 0x1a
+#define DHCP_BROADCAST 0x1c
+#define DHCP_NTP_SERVER 0x2a
+#define DHCP_WINS_SERVER 0x2c
+#define DHCP_REQUESTED_IP 0x32
+#define DHCP_LEASE_TIME 0x33
+#define DHCP_OPTION_OVERLOAD 0x34
+#define DHCP_MESSAGE_TYPE 0x35
+#define DHCP_SERVER_ID 0x36
+#define DHCP_PARAM_REQ 0x37
+#define DHCP_MESSAGE 0x38
+#define DHCP_MAX_SIZE 0x39
+#define DHCP_T1 0x3a
+#define DHCP_T2 0x3b
+#define DHCP_VENDOR 0x3c
+#define DHCP_CLIENT_ID 0x3d
+#define DHCP_FQDN 0x51
+#define DHCP_END 0xFF
+/* Offsets in option byte sequence */
+#define OPT_CODE 0
+#define OPT_LEN 1
+#define OPT_DATA 2
+/* Bits in "overload" option */
+#define OPTION_FIELD 0
+#define FILE_FIELD 1
+#define SNAME_FIELD 2
+
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+#define ETH_10MB 1
+#define ETH_10MB_LEN 6
+
+#define DHCPDISCOVER 1 /* client -> server */
+#define DHCPOFFER 2 /* client <- server */
+#define DHCPREQUEST 3 /* client -> server */
+#define DHCPDECLINE 4 /* client -> server */
+#define DHCPACK 5 /* client <- server */
+#define DHCPNAK 6 /* client <- server */
+#define DHCPRELEASE 7 /* client -> server */
+#define DHCPINFORM 8 /* client -> server */
struct dhcp_option {
- char name[10];
- char flags;
- unsigned char code;
+ uint8_t flags;
+ uint8_t code;
};
-extern struct dhcp_option options[];
-extern int option_lengths[];
+extern const struct dhcp_option dhcp_options[];
+extern const char dhcp_option_strings[];
+extern const uint8_t dhcp_option_lengths[];
+
+uint8_t *get_option(struct dhcpMessage *packet, int code) FAST_FUNC;
+int end_option(uint8_t *optionptr) FAST_FUNC;
+int add_option_string(uint8_t *optionptr, uint8_t *string) FAST_FUNC;
+int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) FAST_FUNC;
+#if ENABLE_FEATURE_UDHCP_RFC3397
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC;
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC;
+#endif
-unsigned char *get_option(struct dhcpMessage *packet, int code);
-int end_option(unsigned char *optionptr);
-int add_option_string(unsigned char *optionptr, unsigned char *string);
-int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data);
-struct option_set *find_option(struct option_set *opt_list, char code);
-void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);
+POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/release/src/router/busybox/networking/udhcp/packet.c b/release/src/router/busybox/networking/udhcp/packet.c
index 0f2a3bc6..33320238 100644
--- a/release/src/router/busybox/networking/udhcp/packet.c
+++ b/release/src/router/busybox/networking/udhcp/packet.c
@@ -1,10 +1,13 @@
-#include <unistd.h>
-#include <string.h>
+/* vi: set sw=4 ts=4: */
+/*
+ * packet.c -- packet ops
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
#include <netinet/in.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <features.h>
-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
@@ -12,27 +15,22 @@
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
-#include <errno.h>
+#include "common.h"
#include "dhcpd.h"
#include "options.h"
-#include "common.h"
+int minpkt = 0; // zzz
-void init_header(struct dhcpMessage *packet, char type)
+void FAST_FUNC udhcp_init_header(struct dhcpMessage *packet, char type)
{
memset(packet, 0, sizeof(struct dhcpMessage));
+ packet->op = BOOTREQUEST; /* if client to a server */
switch (type) {
- case DHCPDISCOVER:
- case DHCPREQUEST:
- case DHCPRELEASE:
- case DHCPINFORM:
- packet->op = BOOTREQUEST;
- break;
case DHCPOFFER:
case DHCPACK:
case DHCPNAK:
- packet->op = BOOTREPLY;
+ packet->op = BOOTREPLY; /* if server to client */
}
packet->htype = ETH_10MB;
packet->hlen = ETH_10MB_LEN;
@@ -43,52 +41,64 @@ void init_header(struct dhcpMessage *packet, char type)
/* read a packet from socket fd, return -1 on read error, -2 on packet error */
-int get_packet(struct dhcpMessage *packet, int fd)
+int FAST_FUNC udhcp_recv_kernel_packet(struct dhcpMessage *packet, int fd)
{
int bytes;
- int i;
- const char broken_vendors[][8] = {
- "MSFT 98",
- ""
- };
- char unsigned *vendor;
+ unsigned char *vendor;
- memset(packet, 0, sizeof(struct dhcpMessage));
- bytes = read(fd, packet, sizeof(struct dhcpMessage));
+ memset(packet, 0, sizeof(*packet));
+ bytes = safe_read(fd, packet, sizeof(*packet));
if (bytes < 0) {
- DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring");
- return -1;
+ DEBUG("cannot read on listening socket, ignoring");
+ return bytes; /* returns -1 */
}
- if (ntohl(packet->cookie) != DHCP_MAGIC) {
- LOG(LOG_ERR, "received bogus message, ignoring");
+ if (packet->cookie != htonl(DHCP_MAGIC)) {
+ bb_error_msg("received bogus message, ignoring");
return -2;
}
- DEBUG(LOG_INFO, "Received a packet");
-
- if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
- for (i = 0; broken_vendors[i][0]; i++) {
- if (vendor[OPT_LEN - 2] == (unsigned char) strlen(broken_vendors[i]) &&
- !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) {
- DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
- broken_vendors[i]);
- packet->flags |= htons(BROADCAST_FLAG);
+ DEBUG("Received a packet");
+
+ if (packet->op == BOOTREQUEST) {
+ vendor = get_option(packet, DHCP_VENDOR);
+ if (vendor) {
+#if 0
+ static const char broken_vendors[][8] = {
+ "MSFT 98",
+ ""
+ };
+ int i;
+ for (i = 0; broken_vendors[i][0]; i++) {
+ if (vendor[OPT_LEN - 2] == (uint8_t)strlen(broken_vendors[i])
+ && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2])
+ ) {
+ DEBUG("broken client (%s), forcing broadcast replies",
+ broken_vendors[i]);
+ packet->flags |= htons(BROADCAST_FLAG);
+ }
}
+#else
+ if (vendor[OPT_LEN - 2] == (uint8_t)(sizeof("MSFT 98")-1)
+ && memcmp(vendor, "MSFT 98", sizeof("MSFT 98")-1) == 0
+ ) {
+ DEBUG("broken client (%s), forcing broadcast replies", "MSFT 98");
+ packet->flags |= htons(BROADCAST_FLAG);
+ }
+#endif
}
}
-
return bytes;
}
-u_int16_t checksum(void *addr, int count)
+uint16_t FAST_FUNC udhcp_checksum(void *addr, int count)
{
/* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
- register int32_t sum = 0;
- u_int16_t *source = (u_int16_t *) addr;
+ int32_t sum = 0;
+ uint16_t *source = (uint16_t *) addr;
while (count > 1) {
/* This is the inner loop */
@@ -100,8 +110,8 @@ u_int16_t checksum(void *addr, int count)
if (count > 0) {
/* Make sure that the left-over byte is added correctly both
* with little and big endian hosts */
- u_int16_t tmp = 0;
- *(unsigned char *) (&tmp) = * (unsigned char *) source;
+ uint16_t tmp = 0;
+ *(uint8_t*)&tmp = *(uint8_t*)source;
sum += tmp;
}
/* Fold 32-bit sum to 16 bits */
@@ -112,90 +122,177 @@ u_int16_t checksum(void *addr, int count)
}
-/* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */
-int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
- u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex)
+/* Construct a ip/udp header for a packet, send packet */
+int FAST_FUNC udhcp_send_raw_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port, const uint8_t *dest_arp,
+ int ifindex)
{
- int fd;
- int result;
struct sockaddr_ll dest;
struct udp_dhcp_packet packet;
+ int fd;
+ int result = -1;
+ const char *msg;
+
+ enum {
+ IP_UPD_DHCP_SIZE = sizeof(struct udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
+ UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE - offsetof(struct udp_dhcp_packet, udp),
+ };
- if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
- DEBUG(LOG_ERR, "socket call failed: %m");
- return -1;
+ fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ if (fd < 0) {
+ msg = "socket(%s)";
+ goto ret_msg;
}
-
+
memset(&dest, 0, sizeof(dest));
memset(&packet, 0, sizeof(packet));
-
+ packet.data = *payload; /* struct copy */
+
dest.sll_family = AF_PACKET;
dest.sll_protocol = htons(ETH_P_IP);
dest.sll_ifindex = ifindex;
dest.sll_halen = 6;
memcpy(dest.sll_addr, dest_arp, 6);
- if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
- DEBUG(LOG_ERR, "bind call failed: %m");
- close(fd);
- return -1;
+ if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
+ msg = "bind(%s)";
+ goto ret_close;
}
+#if 1 // zzz
+ int n;
+
+ // n = size of dhcp only
+ if (minpkt) n = (end_option(payload->options) + 1 + sizeof(*payload) - sizeof(payload->options));
+ else n = sizeof(struct dhcpMessage) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS;
+
packet.ip.protocol = IPPROTO_UDP;
packet.ip.saddr = source_ip;
packet.ip.daddr = dest_ip;
packet.udp.source = htons(source_port);
packet.udp.dest = htons(dest_port);
- packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
+ /* size, excluding IP header: */
+ packet.udp.len = htons(sizeof(packet.udp) + n); // udp + dhcp
+
+ n += sizeof(packet) - sizeof(packet.data); // n = ip + udp + dhcp
+
+ /* for UDP checksumming, ip.len is set to UDP packet len */
packet.ip.tot_len = packet.udp.len;
- memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
- packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
-
- packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
+ packet.udp.check = udhcp_checksum(&packet, n);
+ /* but for sending, it is set to IP packet len */
+ packet.ip.tot_len = htons(n);
packet.ip.ihl = sizeof(packet.ip) >> 2;
packet.ip.version = IPVERSION;
packet.ip.ttl = IPDEFTTL;
- packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
+ packet.ip.check = udhcp_checksum(&packet.ip, sizeof(packet.ip));
+/*
+ bb_info_msg("%s", __FUNCTION__);
+ bb_info_msg("packet.udp.len=%d", ntohs(packet.udp.len));
+ bb_info_msg("packet.ip.tot_len=%d", ntohs(packet.ip.tot_len));
+ bb_info_msg("UPD_DHCP_SIZE=%d", UPD_DHCP_SIZE);
+ bb_info_msg("IP_UPD_DHCP_SIZE=%d", IP_UPD_DHCP_SIZE);
+*/
+ result = sendto(fd, &packet, n, 0, (struct sockaddr *) &dest, sizeof(dest));
+#else
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.saddr = source_ip;
+ packet.ip.daddr = dest_ip;
+ packet.udp.source = htons(source_port);
+ packet.udp.dest = htons(dest_port);
+ /* size, excluding IP header: */
+ packet.udp.len = htons(UPD_DHCP_SIZE);
+ /* for UDP checksumming, ip.len is set to UDP packet len */
+ packet.ip.tot_len = packet.udp.len;
+ packet.udp.check = udhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
+ /* but for sending, it is set to IP packet len */
+ packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
+ packet.ip.ihl = sizeof(packet.ip) >> 2;
+ packet.ip.version = IPVERSION;
+ packet.ip.ttl = IPDEFTTL;
+ packet.ip.check = udhcp_checksum(&packet.ip, sizeof(packet.ip));
- result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
- if (result <= 0) {
- DEBUG(LOG_ERR, "write on socket failed: %m");
- }
+ /* Currently we send full-sized DHCP packets (zero padded).
+ * If you need to change this: last byte of the packet is
+ * packet.data.options[end_option(packet.data.options)]
+ */
+ result = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
+ (struct sockaddr *) &dest, sizeof(dest));
+#endif
+
+ msg = "sendto";
+ ret_close:
close(fd);
+ if (result < 0) {
+ ret_msg:
+ bb_perror_msg(msg, "PACKET");
+ }
return result;
}
/* Let the kernel do all the work for packet generation */
-int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
- u_int32_t dest_ip, int dest_port)
+int FAST_FUNC udhcp_send_kernel_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port)
{
- int n = 1;
- int fd, result;
struct sockaddr_in client;
-
- if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- return -1;
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
- return -1;
+ int fd;
+ int result = -1;
+ const char *msg;
+
+ enum {
+ DHCP_SIZE = sizeof(struct dhcpMessage) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
+ };
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ msg = "socket(%s)";
+ goto ret_msg;
+ }
+ setsockopt_reuseaddr(fd);
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(source_port);
client.sin_addr.s_addr = source_ip;
-
- if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
- return -1;
+ if (bind(fd, (struct sockaddr *)&client, sizeof(client)) == -1) {
+ msg = "bind(%s)";
+ goto ret_close;
+ }
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(dest_port);
- client.sin_addr.s_addr = dest_ip;
+ client.sin_addr.s_addr = dest_ip;
+ if (connect(fd, (struct sockaddr *)&client, sizeof(client)) == -1) {
+ msg = "connect";
+ goto ret_close;
+ }
- if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
- return -1;
+#if 1 // zzz
+ int n;
- result = write(fd, payload, sizeof(struct dhcpMessage));
+ if (minpkt) n = (end_option(payload->options) + 1 + sizeof(*payload) - sizeof(payload->options));
+ else n = DHCP_SIZE;
+/*
+ bb_info_msg("%s n=%d", __FUNCTION__, n);
+ bb_info_msg("%s", __FUNCTION__);
+ bb_info_msg("n=%d", n);
+ bb_info_msg("DHCP_SIZE=%d", DHCP_SIZE);
+*/
+ result = write(fd, payload, n);
+
+#else
+ /* Currently we send full-sized DHCP packets (see above) */
+ result = safe_write(fd, payload, DHCP_SIZE);
+#endif
+
+ msg = "write";
+ ret_close:
close(fd);
+ if (result < 0) {
+ ret_msg:
+ bb_perror_msg(msg, "UDP");
+ }
return result;
-}
+}
diff --git a/release/src/router/busybox/networking/udhcp/packet.h b/release/src/router/busybox/networking/udhcp/packet.h
deleted file mode 100644
index 1a263ef8..00000000
--- a/release/src/router/busybox/networking/udhcp/packet.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _PACKET_H
-#define _PACKET_H
-
-#include <netinet/udp.h>
-#include <netinet/ip.h>
-
-struct dhcpMessage {
- u_int8_t op;
- u_int8_t htype;
- u_int8_t hlen;
- u_int8_t hops;
- u_int32_t xid;
- u_int16_t secs;
- u_int16_t flags;
- u_int32_t ciaddr;
- u_int32_t yiaddr;
- u_int32_t siaddr;
- u_int32_t giaddr;
- u_int8_t chaddr[16];
- u_int8_t sname[64];
- u_int8_t file[128];
- u_int32_t cookie;
- u_int8_t options[308]; /* 312 - cookie */
-};
-
-struct udp_dhcp_packet {
- struct iphdr ip;
- struct udphdr udp;
- struct dhcpMessage data;
-};
-
-void init_header(struct dhcpMessage *packet, char type);
-int get_packet(struct dhcpMessage *packet, int fd);
-u_int16_t checksum(void *addr, int count);
-int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
- u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex);
-int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
- u_int32_t dest_ip, int dest_port);
-
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/pidfile.c b/release/src/router/busybox/networking/udhcp/pidfile.c
deleted file mode 100644
index 246a64aa..00000000
--- a/release/src/router/busybox/networking/udhcp/pidfile.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* pidfile.c
- *
- * Functions to assist in the writing and removing of pidfiles.
- *
- * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "debug.h"
-
-int pidfile_acquire(char *pidfile)
-{
- int pid_fd;
- if (pidfile == NULL) return -1;
-
- pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
- if (pid_fd < 0) {
- LOG(LOG_ERR, "Unable to open pidfile %s: %s\n",
- pidfile, strerror(errno));
- } else {
- lockf(pid_fd, F_LOCK, 0);
- }
-
- return pid_fd;
-}
-
-
-void pidfile_write_release(int pid_fd)
-{
- FILE *out;
-
- if (pid_fd < 0) return;
-
- if ((out = fdopen(pid_fd, "w")) != NULL) {
- fprintf(out, "%d\n", getpid());
- fclose(out);
- }
- lockf(pid_fd, F_UNLCK, 0);
- close(pid_fd);
-}
-
-
-void pidfile_delete(char *pidfile)
-{
- if (pidfile) unlink(pidfile);
-}
-
-
diff --git a/release/src/router/busybox/networking/udhcp/pidfile.h b/release/src/router/busybox/networking/udhcp/pidfile.h
deleted file mode 100644
index 0e2b148a..00000000
--- a/release/src/router/busybox/networking/udhcp/pidfile.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* pidfile.h
- *
- * Functions to assist in the writing and removing of pidfiles.
- *
- * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-int pidfile_acquire(char *pidfile);
-void pidfile_write_release(int pid_fd);
-void pidfile_delete(char *pidfile);
-
diff --git a/release/src/router/busybox/networking/udhcp/script.c b/release/src/router/busybox/networking/udhcp/script.c
index 41b680d6..3029b136 100644
--- a/release/src/router/busybox/networking/udhcp/script.c
+++ b/release/src/router/busybox/networking/udhcp/script.c
@@ -1,45 +1,26 @@
+/* vi: set sw=4 ts=4: */
/* script.c
*
- * Functions to call the DHCP client notification scripts
+ * Functions to call the DHCP client notification scripts
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "options.h"
-#include "dhcpd.h"
+#include "common.h"
#include "dhcpc.h"
#include "options.h"
-#include "common.h"
+
/* get a rough idea of how long an option will be (rounding up...) */
-static const int max_option_length[] = {
+static const uint8_t max_option_length[] = {
[OPTION_IP] = sizeof("255.255.255.255 "),
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
[OPTION_STRING] = 1,
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ [OPTION_STR1035] = 1,
+#endif
[OPTION_BOOLEAN] = sizeof("yes "),
[OPTION_U8] = sizeof("255 "),
[OPTION_U16] = sizeof("65535 "),
@@ -52,57 +33,56 @@ static const int max_option_length[] = {
static inline int upper_length(int length, int opt_index)
{
return max_option_length[opt_index] *
- (length / option_lengths[opt_index]);
+ (length / dhcp_option_lengths[opt_index]);
}
-static int sprintip(char *dest, unsigned char *ip)
-{
- return sprintf(dest, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
-}
-
-static void asprintip(char **dest, char *pre, unsigned char *ip)
+static int sprintip(char *dest, const char *pre, const uint8_t *ip)
{
- asprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
+ return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
}
/* really simple implementation, just count the bits */
-static int mton(struct in_addr *mask)
+static int mton(uint32_t mask)
{
- int i;
- unsigned long bits = ntohl(mask->s_addr);
- /* too bad one can't check the carry bit, etc in c bit
- * shifting */
- for (i = 0; i < 32 && !((bits >> i) & 1); i++);
- return 32 - i;
+ int i = 0;
+ mask = ntohl(mask); /* 111110000-like bit pattern */
+ while (mask) {
+ i++;
+ mask <<= 1;
+ }
+ return i;
}
-/* Fill dest with the text of option 'option'. */
-static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p)
+/* Allocate and fill with the text of option 'option'. */
+static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
{
- int type, optlen;
- u_int16_t val_u16;
+ int len, type, optlen;
+ uint16_t val_u16;
int16_t val_s16;
- u_int32_t val_u32;
+ uint32_t val_u32;
int32_t val_s32;
- int len = option[OPT_LEN - 2];
-
- dest += sprintf(dest, "%s=", type_p->name);
+ char *dest, *ret;
+ len = option[OPT_LEN - 2];
type = type_p->flags & TYPE_MASK;
- optlen = option_lengths[type];
- for(;;) {
+ optlen = dhcp_option_lengths[type];
+
+ dest = ret = xmalloc(upper_length(len, type) + strlen(opt_name) + 2);
+ dest += sprintf(ret, "%s=", opt_name);
+
+ for (;;) {
switch (type) {
case OPTION_IP_PAIR:
- dest += sprintip(dest, option);
- *(dest++) = '/';
+ dest += sprintip(dest, "", option);
+ *dest++ = '/';
option += 4;
optlen = 4;
case OPTION_IP: /* Works regardless of host byte order. */
- dest += sprintip(dest, option);
- break;
+ dest += sprintip(dest, "", option);
+ break;
case OPTION_BOOLEAN:
dest += sprintf(dest, *option ? "yes" : "no");
break;
@@ -110,134 +90,146 @@ static void fill_options(char *dest, unsigned char *option, struct dhcp_option *
dest += sprintf(dest, "%u", *option);
break;
case OPTION_U16:
- memcpy(&val_u16, option, 2);
+ move_from_unaligned16(val_u16, option);
dest += sprintf(dest, "%u", ntohs(val_u16));
break;
case OPTION_S16:
- memcpy(&val_s16, option, 2);
+ move_from_unaligned16(val_s16, option);
dest += sprintf(dest, "%d", ntohs(val_s16));
break;
case OPTION_U32:
- memcpy(&val_u32, option, 4);
+ move_from_unaligned32(val_u32, option);
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
break;
case OPTION_S32:
- memcpy(&val_s32, option, 4);
+ move_from_unaligned32(val_s32, option);
dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
break;
case OPTION_STRING:
memcpy(dest, option, len);
dest[len] = '\0';
- return; /* Short circuit this case */
+ return ret; /* Short circuit this case */
+#if ENABLE_FEATURE_UDHCP_RFC3397
+ case OPTION_STR1035:
+ /* unpack option into dest; use ret for prefix (i.e., "optname=") */
+ dest = dname_dec(option, len, ret);
+ free(ret);
+ return dest;
+#endif
}
option += optlen;
len -= optlen;
- if (len <= 0) break;
+ if (len <= 0)
+ break;
dest += sprintf(dest, " ");
}
+ return ret;
}
-static char *find_env(const char *prefix, char *defaultstr)
-{
- char *ptr;
-
- ptr = getenv(prefix);
- return ptr ? ptr : defaultstr;
-}
-
-
-/* put all the paramaters into an environment */
+/* put all the parameters into an environment */
static char **fill_envp(struct dhcpMessage *packet)
{
int num_options = 0;
- int i, j;
- char **envp;
- unsigned char *temp;
- struct in_addr subnet;
- char over = 0;
-
- if (packet == NULL)
- num_options = 0;
- else {
- for (i = 0; options[i].code; i++)
- if (get_option(packet, options[i].code))
+ int i;
+ char **envp, **curr;
+ const char *opt_name;
+ uint8_t *temp;
+ uint8_t over = 0;
+
+ if (packet) {
+ for (i = 0; dhcp_options[i].code; i++) {
+ if (get_option(packet, dhcp_options[i].code)) {
num_options++;
- if (packet->siaddr) num_options++;
- if ((temp = get_option(packet, DHCP_OPTION_OVER)))
+ if (dhcp_options[i].code == DHCP_SUBNET)
+ num_options++; /* for mton */
+ }
+ }
+ if (packet->siaddr)
+ num_options++;
+ temp = get_option(packet, DHCP_OPTION_OVERLOAD);
+ if (temp)
over = *temp;
- if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
- if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
- }
-
- envp = xmalloc((num_options + 5) * sizeof(char *));
- j = 0;
- asprintf(&envp[j++], "interface=%s", client_config.interface);
- envp[j++] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin");
- envp[j++] = find_env("HOME", "HOME=/");
-
- if (packet == NULL) {
- envp[j++] = NULL;
- return envp;
+ if (!(over & FILE_FIELD) && packet->file[0])
+ num_options++;
+ if (!(over & SNAME_FIELD) && packet->sname[0])
+ num_options++;
}
- asprintip(&envp[j++], "ip=", (unsigned char *) &packet->yiaddr);
+ curr = envp = xzalloc(sizeof(char *) * (num_options + 3));
+ *curr = xasprintf("interface=%s", client_config.interface);
+ putenv(*curr++);
+ if (packet == NULL)
+ return envp;
- for (i = 0; options[i].code; i++) {
- if (!(temp = get_option(packet, options[i].code)))
- continue;
- envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], options[i].flags & TYPE_MASK) + strlen(options[i].name) + 2);
- fill_options(envp[j++], temp, &options[i]);
+ *curr = xmalloc(sizeof("ip=255.255.255.255"));
+ sprintip(*curr, "ip=", (uint8_t *) &packet->yiaddr);
+ putenv(*curr++);
+
+ opt_name = dhcp_option_strings;
+ i = 0;
+ while (*opt_name) {
+ temp = get_option(packet, dhcp_options[i].code);
+ if (!temp)
+ goto next;
+ *curr = alloc_fill_opts(temp, &dhcp_options[i], opt_name);
+ putenv(*curr++);
/* Fill in a subnet bits option for things like /24 */
- if (options[i].code == DHCP_SUBNET) {
- memcpy(&subnet, temp, 4);
- asprintf(&envp[j++], "mask=%d", mton(&subnet));
+ if (dhcp_options[i].code == DHCP_SUBNET) {
+ uint32_t subnet;
+ move_from_unaligned32(subnet, temp);
+ *curr = xasprintf("mask=%d", mton(subnet));
+ putenv(*curr++);
}
+ next:
+ opt_name += strlen(opt_name) + 1;
+ i++;
}
if (packet->siaddr) {
- asprintip(&envp[j++], "siaddr=", (unsigned char *) &packet->siaddr);
+ *curr = xmalloc(sizeof("siaddr=255.255.255.255"));
+ sprintip(*curr, "siaddr=", (uint8_t *) &packet->siaddr);
+ putenv(*curr++);
}
if (!(over & FILE_FIELD) && packet->file[0]) {
/* watch out for invalid packets */
packet->file[sizeof(packet->file) - 1] = '\0';
- asprintf(&envp[j++], "boot_file=%s", packet->file);
+ *curr = xasprintf("boot_file=%s", packet->file);
+ putenv(*curr++);
}
if (!(over & SNAME_FIELD) && packet->sname[0]) {
/* watch out for invalid packets */
packet->sname[sizeof(packet->sname) - 1] = '\0';
- asprintf(&envp[j++], "sname=%s", packet->sname);
- }
- envp[j] = NULL;
+ *curr = xasprintf("sname=%s", packet->sname);
+ putenv(*curr++);
+ }
return envp;
}
/* Call a script with a par file and env vars */
-void run_script(struct dhcpMessage *packet, const char *name)
+void FAST_FUNC udhcp_run_script(struct dhcpMessage *packet, const char *name)
{
- int pid;
- char **envp;
+ char **envp, **curr;
+ char *argv[3];
if (client_config.script == NULL)
return;
+ DEBUG("vfork'ing and exec'ing %s", client_config.script);
+
+ envp = fill_envp(packet);
+
/* call script */
- pid = fork();
- if (pid) {
- waitpid(pid, NULL, 0);
- return;
- } else if (pid == 0) {
- envp = fill_envp(packet);
-
- /* close fd's? */
-
- /* exec script */
- DEBUG(LOG_INFO, "execle'ing %s", client_config.script);
- execle(client_config.script, client_config.script,
- name, NULL, envp);
- LOG(LOG_ERR, "script %s failed: %m", client_config.script);
- exit(1);
- }
+ argv[0] = (char*) client_config.script;
+ argv[1] = (char*) name;
+ argv[2] = NULL;
+ wait4pid(spawn(argv));
+
+ for (curr = envp; *curr; curr++) {
+ bb_unsetenv(*curr);
+ free(*curr);
+ }
+ free(envp);
}
diff --git a/release/src/router/busybox/networking/udhcp/script.h b/release/src/router/busybox/networking/udhcp/script.h
deleted file mode 100644
index 87a20cc1..00000000
--- a/release/src/router/busybox/networking/udhcp/script.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SCRIPT_H
-#define _SCRIPT_H
-
-void run_script(struct dhcpMessage *packet, const char *name);
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/serverpacket.c b/release/src/router/busybox/networking/udhcp/serverpacket.c
index 09682444..8b0f1856 100644
--- a/release/src/router/busybox/networking/udhcp/serverpacket.c
+++ b/release/src/router/busybox/networking/udhcp/serverpacket.c
@@ -1,6 +1,7 @@
+/* vi: set sw=4 ts=4: */
/* serverpacket.c
*
- * Constuct and send DHCP server packets
+ * Construct and send DHCP server packets
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
@@ -19,22 +20,18 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <time.h>
-
+#include "common.h"
+#include "dhcpc.h"
#include "dhcpd.h"
#include "options.h"
-#include "common.h"
+
/* send a packet to giaddr using the kernel ip stack */
static int send_packet_to_relay(struct dhcpMessage *payload)
{
- DEBUG(LOG_INFO, "Forwarding packet to relay");
+ DEBUG("Forwarding packet to relay");
- return kernel_packet(payload, server_config.server, SERVER_PORT,
+ return udhcp_send_kernel_packet(payload, server_config.server, SERVER_PORT,
payload->giaddr, SERVER_PORT);
}
@@ -42,46 +39,45 @@ static int send_packet_to_relay(struct dhcpMessage *payload)
/* send a packet to a specific arp address and ip address by creating our own ip packet */
static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
{
- unsigned char *chaddr;
- u_int32_t ciaddr;
-
+ const uint8_t *chaddr;
+ uint32_t ciaddr;
+
if (force_broadcast) {
- DEBUG(LOG_INFO, "broadcasting packet to client (NAK)");
+ DEBUG("broadcasting packet to client (NAK)");
ciaddr = INADDR_BROADCAST;
chaddr = MAC_BCAST_ADDR;
} else if (payload->ciaddr) {
- DEBUG(LOG_INFO, "unicasting packet to client ciaddr");
+ DEBUG("unicasting packet to client ciaddr");
ciaddr = payload->ciaddr;
chaddr = payload->chaddr;
- } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
- DEBUG(LOG_INFO, "broadcasting packet to client (requested)");
+ } else if (payload->flags & htons(BROADCAST_FLAG)) {
+ DEBUG("broadcasting packet to client (requested)");
ciaddr = INADDR_BROADCAST;
chaddr = MAC_BCAST_ADDR;
} else {
- DEBUG(LOG_INFO, "unicasting packet to client yiaddr");
+ DEBUG("unicasting packet to client yiaddr");
ciaddr = payload->yiaddr;
chaddr = payload->chaddr;
}
- return raw_packet(payload, server_config.server, SERVER_PORT,
- ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
+ return udhcp_send_raw_packet(payload,
+ /*src*/ server_config.server, SERVER_PORT,
+ /*dst*/ ciaddr, CLIENT_PORT, chaddr,
+ server_config.ifindex);
}
/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
static int send_packet(struct dhcpMessage *payload, int force_broadcast)
{
- int ret;
-
if (payload->giaddr)
- ret = send_packet_to_relay(payload);
- else ret = send_packet_to_client(payload, force_broadcast);
- return ret;
+ return send_packet_to_relay(payload);
+ return send_packet_to_client(payload, force_broadcast);
}
static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
{
- init_header(packet, type);
+ udhcp_init_header(packet, type);
packet->xid = oldpacket->xid;
memcpy(packet->chaddr, oldpacket->chaddr, 16);
packet->flags = oldpacket->flags;
@@ -96,78 +92,82 @@ static void add_bootp_options(struct dhcpMessage *packet)
{
packet->siaddr = server_config.siaddr;
if (server_config.sname)
- strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1);
+ strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
if (server_config.boot_file)
- strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1);
+ strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
}
-
+
/* send a DHCP OFFER to a DHCP DISCOVER */
-int sendOffer(struct dhcpMessage *oldpacket)
+int FAST_FUNC send_offer(struct dhcpMessage *oldpacket)
{
struct dhcpMessage packet;
- struct dhcpOfferedAddr *lease = NULL;
- u_int32_t req_align, lease_time_align = server_config.lease;
- unsigned char *req, *lease_time;
+ uint32_t req_align;
+ uint32_t lease_time_aligned = server_config.lease;
+ uint32_t static_lease_ip;
+ uint8_t *req, *lease_time, *p_host_name;
struct option_set *curr;
struct in_addr addr;
init_packet(&packet, oldpacket, DHCPOFFER);
-
+
+ static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr);
+
/* ADDME: if static, short circuit */
- /* the client is in our lease/offered table */
- if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) {
- if (!lease_expired(lease))
- lease_time_align = lease->expires - time(0);
- packet.yiaddr = lease->yiaddr;
-
- /* Or the client has a requested ip */
- } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) &&
-
- /* Don't look here (ugly hackish thing to do) */
- memcpy(&req_align, req, 4) &&
-
- /* and the ip is in the lease range */
- ntohl(req_align) >= ntohl(server_config.start) &&
- ntohl(req_align) <= ntohl(server_config.end) &&
-
- /* and its not already taken/offered */ /* ADDME: check that its not a static lease */
- ((!(lease = find_lease_by_yiaddr(req_align)) ||
-
- /* or its taken, but expired */ /* ADDME: or maybe in here */
- lease_expired(lease)))) {
- packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */
-
- /* otherwise, find a free IP */ /*ADDME: is it a static lease? */
+ if (!static_lease_ip) {
+ struct dhcpOfferedAddr *lease;
+
+ lease = find_lease_by_chaddr(oldpacket->chaddr);
+ /* the client is in our lease/offered table */
+ if (lease) {
+ signed_leasetime_t tmp = lease->expires - time(NULL);
+ if (tmp >= 0)
+ lease_time_aligned = tmp;
+ packet.yiaddr = lease->yiaddr;
+ /* Or the client has requested an ip */
+ } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
+ /* Don't look here (ugly hackish thing to do) */
+ && (move_from_unaligned32(req_align, req), 1)
+ /* and the ip is in the lease range */
+ && ntohl(req_align) >= server_config.start_ip
+ && ntohl(req_align) <= server_config.end_ip
+ /* and is not already taken/offered */
+ && (!(lease = find_lease_by_yiaddr(req_align))
+ /* or its taken, but expired */
+ || lease_expired(lease))
+ ) {
+ packet.yiaddr = req_align;
+ /* otherwise, find a free IP */
+ } else {
+ packet.yiaddr = find_free_or_expired_address();
+ }
+
+ if (!packet.yiaddr) {
+ bb_error_msg("no IP addresses to give - OFFER abandoned");
+ return -1;
+ }
+ p_host_name = get_option(oldpacket, DHCP_HOST_NAME);
+ if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time, p_host_name)) {
+ bb_error_msg("lease pool is full - OFFER abandoned");
+ return -1;
+ }
+ lease_time = get_option(oldpacket, DHCP_LEASE_TIME);
+ if (lease_time) {
+ move_from_unaligned32(lease_time_aligned, lease_time);
+ lease_time_aligned = ntohl(lease_time_aligned);
+ if (lease_time_aligned > server_config.lease)
+ lease_time_aligned = server_config.lease;
+ }
+
+ /* Make sure we aren't just using the lease time from the previous offer */
+ if (lease_time_aligned < server_config.min_lease)
+ lease_time_aligned = server_config.min_lease;
} else {
- packet.yiaddr = find_address(0);
-
- /* try for an expired lease */
- if (!packet.yiaddr) packet.yiaddr = find_address(1);
- }
-
- if(!packet.yiaddr) {
- LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned");
- return -1;
- }
-
- if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
- LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned");
- return -1;
- }
-
- if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
- memcpy(&lease_time_align, lease_time, 4);
- lease_time_align = ntohl(lease_time_align);
- if (lease_time_align > server_config.lease)
- lease_time_align = server_config.lease;
+ /* It is a static lease... use it */
+ packet.yiaddr = static_lease_ip;
}
- /* Make sure we aren't just using the lease time from the previous offer */
- if (lease_time_align < server_config.min_lease)
- lease_time_align = server_config.lease;
- /* ADDME: end of short circuit */
- add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+ add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_aligned));
curr = server_config.options;
while (curr) {
@@ -177,46 +177,48 @@ int sendOffer(struct dhcpMessage *oldpacket)
}
add_bootp_options(&packet);
-
+
addr.s_addr = packet.yiaddr;
- LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr));
+ bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
return send_packet(&packet, 0);
}
-int sendNAK(struct dhcpMessage *oldpacket)
+int FAST_FUNC send_NAK(struct dhcpMessage *oldpacket)
{
struct dhcpMessage packet;
init_packet(&packet, oldpacket, DHCPNAK);
-
- DEBUG(LOG_INFO, "sending NAK");
+
+ DEBUG("Sending NAK");
return send_packet(&packet, 1);
}
-int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr)
+int FAST_FUNC send_ACK(struct dhcpMessage *oldpacket, uint32_t yiaddr)
{
struct dhcpMessage packet;
struct option_set *curr;
- unsigned char *lease_time;
- u_int32_t lease_time_align = server_config.lease;
+ uint8_t *lease_time;
+ uint32_t lease_time_aligned = server_config.lease;
struct in_addr addr;
+ uint8_t *p_host_name;
init_packet(&packet, oldpacket, DHCPACK);
packet.yiaddr = yiaddr;
-
- if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
- memcpy(&lease_time_align, lease_time, 4);
- lease_time_align = ntohl(lease_time_align);
- if (lease_time_align > server_config.lease)
- lease_time_align = server_config.lease;
- else if (lease_time_align < server_config.min_lease)
- lease_time_align = server_config.lease;
+
+ lease_time = get_option(oldpacket, DHCP_LEASE_TIME);
+ if (lease_time) {
+ move_from_unaligned32(lease_time_aligned, lease_time);
+ lease_time_aligned = ntohl(lease_time_aligned);
+ if (lease_time_aligned > server_config.lease)
+ lease_time_aligned = server_config.lease;
+ else if (lease_time_aligned < server_config.min_lease)
+ lease_time_aligned = server_config.min_lease;
}
-
- add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
-
+
+ add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_aligned));
+
curr = server_config.options;
while (curr) {
if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
@@ -227,24 +229,29 @@ int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr)
add_bootp_options(&packet);
addr.s_addr = packet.yiaddr;
- LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr));
+ bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
- if (send_packet(&packet, 0) < 0)
+ if (send_packet(&packet, 0) < 0)
return -1;
- add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
+ p_host_name = get_option(oldpacket, DHCP_HOST_NAME);
+ add_lease(packet.chaddr, packet.yiaddr, lease_time_aligned, p_host_name);
+ if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
+ /* rewrite the file with leases at every new acceptance */
+ write_leases();
+ }
return 0;
}
-int send_inform(struct dhcpMessage *oldpacket)
+int FAST_FUNC send_inform(struct dhcpMessage *oldpacket)
{
struct dhcpMessage packet;
struct option_set *curr;
init_packet(&packet, oldpacket, DHCPACK);
-
+
curr = server_config.options;
while (curr) {
if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
diff --git a/release/src/router/busybox/networking/udhcp/serverpacket.h b/release/src/router/busybox/networking/udhcp/serverpacket.h
deleted file mode 100644
index 5a4fb276..00000000
--- a/release/src/router/busybox/networking/udhcp/serverpacket.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _SERVERPACKET_H
-#define _SERVERPACKET_H
-
-
-int sendOffer(struct dhcpMessage *oldpacket);
-int sendNAK(struct dhcpMessage *oldpacket);
-int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr);
-int send_inform(struct dhcpMessage *oldpacket);
-
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/signalpipe.c b/release/src/router/busybox/networking/udhcp/signalpipe.c
new file mode 100644
index 00000000..a025bd8b
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/signalpipe.c
@@ -0,0 +1,82 @@
+/* vi: set sw=4 ts=4: */
+/* signalpipe.c
+ *
+ * Signal pipe infrastructure. A reliable way of delivering signals.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "common.h"
+
+
+static struct fd_pair signal_pipe;
+
+static void signal_handler(int sig)
+{
+ unsigned char ch = sig; /* use char, avoid dealing with partial writes */
+ if (write(signal_pipe.wr, &ch, 1) != 1)
+ bb_perror_msg("cannot send signal");
+}
+
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+void FAST_FUNC udhcp_sp_setup(void)
+{
+ /* was socketpair, but it needs AF_UNIX in kernel */
+ xpiped_pair(signal_pipe);
+ close_on_exec_on(signal_pipe.rd);
+ close_on_exec_on(signal_pipe.wr);
+ ndelay_on(signal_pipe.wr);
+ bb_signals(0
+ + (1 << SIGUSR1)
+ + (1 << SIGUSR2)
+ + (1 << SIGTERM)
+ , signal_handler);
+}
+
+
+/* Quick little function to setup the rfds. Will return the
+ * max_fd for use with select. Limited in that you can only pass
+ * one extra fd */
+int FAST_FUNC udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
+{
+ FD_ZERO(rfds);
+ FD_SET(signal_pipe.rd, rfds);
+ if (extra_fd >= 0) {
+ close_on_exec_on(extra_fd);
+ FD_SET(extra_fd, rfds);
+ }
+ return signal_pipe.rd > extra_fd ? signal_pipe.rd : extra_fd;
+}
+
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int FAST_FUNC udhcp_sp_read(const fd_set *rfds)
+{
+ unsigned char sig;
+
+ if (!FD_ISSET(signal_pipe.rd, rfds))
+ return 0;
+
+ if (safe_read(signal_pipe.rd, &sig, 1) != 1)
+ return -1;
+
+ return sig;
+}
diff --git a/release/src/router/busybox/networking/udhcp/socket.c b/release/src/router/busybox/networking/udhcp/socket.c
index a51a7436..edf4355b 100644
--- a/release/src/router/busybox/networking/udhcp/socket.c
+++ b/release/src/router/busybox/networking/udhcp/socket.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* socket.c -- DHCP server client/server socket creation
*
@@ -22,17 +23,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#include <string.h>
-#include <arpa/inet.h>
#include <net/if.h>
-#include <errno.h>
#include <features.h>
-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
@@ -43,113 +36,76 @@
#include "common.h"
-int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp)
+
+int FAST_FUNC udhcp_read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
{
int fd;
struct ifreq ifr;
struct sockaddr_in *our_ip;
- memset(&ifr, 0, sizeof(struct ifreq));
- if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
- ifr.ifr_addr.sa_family = AF_INET;
- strcpy(ifr.ifr_name, interface);
+ memset(&ifr, 0, sizeof(ifr));
+ fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
- if (addr) {
- if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
- our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
- *addr = our_ip->sin_addr.s_addr;
- DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
- } else {
- LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m");
- return -1;
- }
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy_IFNAMSIZ(ifr.ifr_name, interface);
+ if (addr) {
+ if (ioctl_or_perror(fd, SIOCGIFADDR, &ifr,
+ "is interface %s up and configured?", interface)
+ ) {
+ close(fd);
+ return -1;
}
-
- if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
- DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
- *ifindex = ifr.ifr_ifindex;
- } else {
- LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m");
+ our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+ *addr = our_ip->sin_addr.s_addr;
+ DEBUG("ip of %s = %s", interface, inet_ntoa(our_ip->sin_addr));
+ }
+
+ if (ifindex) {
+ if (ioctl_or_warn(fd, SIOCGIFINDEX, &ifr) != 0) {
+ close(fd);
return -1;
}
- if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
- memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
- DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
- arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
- } else {
- LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m");
+ DEBUG("adapter index %d", ifr.ifr_ifindex);
+ *ifindex = ifr.ifr_ifindex;
+ }
+
+ if (arp) {
+ if (ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr) != 0) {
+ close(fd);
return -1;
}
- } else {
- LOG(LOG_ERR, "socket failed!: %m");
- return -1;
+ memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
+ DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
+ arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
}
+
close(fd);
return 0;
}
-
-int listen_socket(unsigned int ip, int port, char *inf)
+/* 1. None of the callers expects it to ever fail */
+/* 2. ip was always INADDR_ANY */
+int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf)
{
- struct ifreq interface;
int fd;
struct sockaddr_in addr;
- int n = 1;
-
- DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf);
- if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
- DEBUG(LOG_ERR, "socket call failed: %m");
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = ip;
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) {
- close(fd);
- return -1;
- }
- if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) {
- close(fd);
- return -1;
- }
-
- strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ);
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) {
- close(fd);
- return -1;
- }
- if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
- close(fd);
- return -1;
- }
-
- return fd;
-}
+ DEBUG("Opening listen socket on *:%d %s", port, inf);
+ fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ setsockopt_reuseaddr(fd);
+ if (setsockopt_broadcast(fd) == -1)
+ bb_perror_msg_and_die("SO_BROADCAST");
-int raw_socket(int ifindex)
-{
- int fd;
- struct sockaddr_ll sock;
+ /* NB: bug 1032 says this doesn't work on ethernet aliases (ethN:M) */
+ if (setsockopt_bindtodevice(fd, inf))
+ xfunc_die(); /* warning is already printed */
- DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex);
- if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
- DEBUG(LOG_ERR, "socket call failed: %m");
- return -1;
- }
-
- sock.sll_family = AF_PACKET;
- sock.sll_protocol = htons(ETH_P_IP);
- sock.sll_ifindex = ifindex;
- if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
- DEBUG(LOG_ERR, "bind call failed: %m");
- close(fd);
- return -1;
- }
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ /* addr.sin_addr.s_addr = ip; - all-zeros is INADDR_ANY */
+ xbind(fd, (struct sockaddr *)&addr, sizeof(addr));
return fd;
}
diff --git a/release/src/router/busybox/networking/udhcp/socket.h b/release/src/router/busybox/networking/udhcp/socket.h
deleted file mode 100644
index 333994b8..00000000
--- a/release/src/router/busybox/networking/udhcp/socket.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* socket.h */
-#ifndef _SOCKET_H
-#define _SOCKET_H
-
-int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp);
-int listen_socket(unsigned int ip, int port, char *inf);
-int raw_socket(int ifindex);
-
-#endif
diff --git a/release/src/router/busybox/networking/udhcp/static_leases.c b/release/src/router/busybox/networking/udhcp/static_leases.c
new file mode 100644
index 00000000..1e77a58f
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/static_leases.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * static_leases.c -- Couple of functions to assist with storing and
+ * retrieving data for static leases
+ *
+ * Wade Berrier <wberrier@myrealbox.com> September 2004
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+/* Takes the address of the pointer to the static_leases linked list,
+ * Address to a 6 byte mac address
+ * Address to a 4 byte ip address */
+void FAST_FUNC addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t ip)
+{
+ struct static_lease *new_static_lease;
+
+ /* Build new node */
+ new_static_lease = xzalloc(sizeof(struct static_lease));
+ memcpy(new_static_lease->mac, mac, 6);
+ new_static_lease->ip = ip;
+ /*new_static_lease->next = NULL;*/
+
+ /* If it's the first node to be added... */
+ if (*lease_struct == NULL) {
+ *lease_struct = new_static_lease;
+ } else {
+ struct static_lease *cur = *lease_struct;
+ while (cur->next)
+ cur = cur->next;
+ cur->next = new_static_lease;
+ }
+}
+
+/* Check to see if a mac has an associated static lease */
+uint32_t FAST_FUNC getIpByMac(struct static_lease *lease_struct, void *mac)
+{
+ while (lease_struct) {
+ if (memcmp(lease_struct->mac, mac, 6) == 0)
+ return lease_struct->ip;
+ lease_struct = lease_struct->next;
+ }
+
+ return 0;
+}
+
+/* Check to see if an ip is reserved as a static ip */
+int FAST_FUNC reservedIp(struct static_lease *lease_struct, uint32_t ip)
+{
+ while (lease_struct) {
+ if (lease_struct->ip == ip)
+ return 1;
+ lease_struct = lease_struct->next;
+ }
+
+ return 0;
+}
+
+#if ENABLE_UDHCP_DEBUG
+/* Print out static leases just to check what's going on */
+/* Takes the address of the pointer to the static_leases linked list */
+void FAST_FUNC printStaticLeases(struct static_lease **arg)
+{
+ struct static_lease *cur = *arg;
+
+ while (cur) {
+ printf("PrintStaticLeases: Lease mac Value: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ cur->mac[0], cur->mac[1], cur->mac[2],
+ cur->mac[3], cur->mac[4], cur->mac[5]
+ );
+ printf("PrintStaticLeases: Lease ip Value: %x\n", cur->ip);
+ cur = cur->next;
+ }
+}
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/version.h b/release/src/router/busybox/networking/udhcp/version.h
deleted file mode 100644
index 3862539f..00000000
--- a/release/src/router/busybox/networking/udhcp/version.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _UDHCP_VERSION_H
-#define _UDHCP_VERSION_H
-
-#define VERSION "0.9.9-pre"
-
-#endif
diff --git a/release/src/router/busybox/networking/vconfig.c b/release/src/router/busybox/networking/vconfig.c
index d58c375f..00379fc2 100644
--- a/release/src/router/busybox/networking/vconfig.c
+++ b/release/src/router/busybox/networking/vconfig.c
@@ -4,51 +4,67 @@
*
* Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 N/A */
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
+#include "libbb.h"
#include <net/if.h>
-#include <linux/if_vlan.h>
-#include <string.h>
-#include <limits.h>
-#include "busybox.h"
+
+/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */
+enum vlan_ioctl_cmds {
+ ADD_VLAN_CMD,
+ DEL_VLAN_CMD,
+ SET_VLAN_INGRESS_PRIORITY_CMD,
+ SET_VLAN_EGRESS_PRIORITY_CMD,
+ GET_VLAN_INGRESS_PRIORITY_CMD,
+ GET_VLAN_EGRESS_PRIORITY_CMD,
+ SET_VLAN_NAME_TYPE_CMD,
+ SET_VLAN_FLAG_CMD
+};
+enum vlan_name_types {
+ VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */
+ VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */
+ VLAN_NAME_TYPE_HIGHEST
+};
+
+struct vlan_ioctl_args {
+ int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
+ char device1[24];
+
+ union {
+ char device2[24];
+ int VID;
+ unsigned int skb_priority;
+ unsigned int name_type;
+ unsigned int bind_type;
+ unsigned int flag; /* Matches vlan_dev_info flags */
+ } u;
+
+ short vlan_qos;
+};
#define VLAN_GROUP_ARRAY_LEN 4096
-#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */
+#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */
-/* On entry, table points to the length of the current string plus
- * nul terminator plus data length for the subsequent entry. The
- * return value is the last data entry for the matching string. */
+/* On entry, table points to the length of the current string
+ * plus NUL terminator plus data length for the subsequent entry.
+ * The return value is the last data entry for the matching string. */
static const char *xfind_str(const char *table, const char *str)
{
while (strcasecmp(str, table+1) != 0) {
- if (!*(table += table[0])) {
+ table += table[0];
+ if (!*table) {
bb_show_usage();
}
}
return table - 1;
}
-static const char cmds[] = {
+static const char cmds[] ALIGN1 = {
4, ADD_VLAN_CMD, 7,
'a', 'd', 'd', 0,
3, DEL_VLAN_CMD, 7,
@@ -57,7 +73,7 @@ static const char cmds[] = {
's', 'e', 't', '_',
'n', 'a', 'm', 'e', '_',
't', 'y', 'p', 'e', 0,
- 4, SET_VLAN_FLAG_CMD, 12,
+ 5, SET_VLAN_FLAG_CMD, 12,
's', 'e', 't', '_',
'f', 'l', 'a', 'g', 0,
5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
@@ -70,13 +86,13 @@ static const char cmds[] = {
'm', 'a', 'p', 0,
};
-static const char name_types[] = {
+static const char name_types[] ALIGN1 = {
VLAN_NAME_TYPE_PLUS_VID, 16,
'V', 'L', 'A', 'N',
'_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
0,
VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
- 'V', 'L', 'A', 'N',
+ 'V', 'L', 'A', 'N',
'_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
'_', 'N', 'O', '_', 'P', 'A', 'D', 0,
VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
@@ -89,8 +105,9 @@ static const char name_types[] = {
'_', 'N', 'O', '_', 'P', 'A', 'D', 0,
};
-static const char conf_file_name[] = "/proc/net/vlan/config";
+static const char conf_file_name[] ALIGN1 = "/proc/net/vlan/config";
+int vconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int vconfig_main(int argc, char **argv)
{
struct vlan_ioctl_args ifr;
@@ -102,11 +119,10 @@ int vconfig_main(int argc, char **argv)
}
/* Don't bother closing the filedes. It will be closed on cleanup. */
- if (open(conf_file_name, O_RDONLY) < 0) { /* Is 802.1q is present? */
- bb_perror_msg_and_die("open %s", conf_file_name);
- }
+ /* Will die if 802.1q is not present */
+ xopen(conf_file_name, O_RDONLY);
- memset(&ifr, 0, sizeof(struct vlan_ioctl_args));
+ memset(&ifr, 0, sizeof(ifr));
++argv;
p = xfind_str(cmds+2, *argv);
@@ -118,10 +134,7 @@ int vconfig_main(int argc, char **argv)
if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */
ifr.u.name_type = *xfind_str(name_types+1, argv[1]);
} else {
- if (strlen(argv[1]) >= IF_NAMESIZE) {
- bb_error_msg_and_die("if_name >= %d chars\n", IF_NAMESIZE);
- }
- strcpy(ifr.device1, argv[1]);
+ strncpy_IFNAMSIZ(ifr.device1, argv[1]);
p = argv[2];
/* I suppose one could try to combine some of the function calls below,
@@ -130,21 +143,20 @@ int vconfig_main(int argc, char **argv)
* doing so wouldn't save that much space and would also make maintainence
* more of a pain. */
if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */
- ifr.u.flag = bb_xgetularg10_bnd(p, 0, 1);
+ ifr.u.flag = xatoul_range(p, 0, 1);
+ /* DM: in order to set reorder header, qos must be set */
+ ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
} else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
- ifr.u.VID = bb_xgetularg10_bnd(p, 0, VLAN_GROUP_ARRAY_LEN-1);
+ ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1);
} else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
- ifr.u.skb_priority = bb_xgetularg10_bnd(p, 0, ULONG_MAX);
- ifr.vlan_qos = bb_xgetularg10_bnd(argv[3], 0, 7);
+ ifr.u.skb_priority = xatou(p);
+ ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
}
}
- if (((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- || (ioctl(fd, SIOCSIFVLAN, &ifr) < 0)
- ) {
- bb_perror_msg_and_die("socket or ioctl error for %s", *argv);
- }
+ fd = xsocket(AF_INET, SOCK_STREAM, 0);
+ ioctl_or_perror_and_die(fd, SIOCSIFVLAN, &ifr,
+ "ioctl error for %s", *argv);
return 0;
}
-
diff --git a/release/src/router/busybox/networking/wget.c b/release/src/router/busybox/networking/wget.c
index f1d363d2..48759049 100644
--- a/release/src/router/busybox/networking/wget.c
+++ b/release/src/router/busybox/networking/wget.c
@@ -4,97 +4,215 @@
*
* Chip Rosenthal Covad Communications <chip@laserlink.net>
*
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <getopt.h>
-
-#include "busybox.h"
+#include "libbb.h"
struct host_info {
- char *host;
- int port;
- char *path;
- int is_ftp;
- char *user;
+ // May be used if we ever will want to free() all xstrdup()s...
+ /* char *allocated; */
+ const char *path;
+ const char *user;
+ char *host;
+ int port;
+ smallint is_ftp;
};
-static void parse_url(char *url, struct host_info *h);
-static FILE *open_socket(char *host, int port);
-static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
-static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);
-
-/* Globals (can be accessed from signal handlers */
-static off_t filesize = 0; /* content-length of the file */
-static int chunked = 0; /* chunked transfer encoding */
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
-static void progressmeter(int flag);
-static char *curfile; /* Name of current file being transferred. */
-static struct timeval start; /* Time a transfer started. */
-static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */
-/* For progressmeter() -- number of seconds before xfer considered "stalled" */
-static const int STALLTIME = 5;
+
+/* Globals (can be accessed from signal handlers) */
+struct globals {
+ off_t content_len; /* Content-length of the file */
+ off_t beg_range; /* Range at which continue begins */
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ off_t lastsize;
+ off_t totalsize;
+ off_t transferred; /* Number of bytes transferred so far */
+ const char *curfile; /* Name of current file being transferred */
+ unsigned lastupdate_sec;
+ unsigned start_sec;
#endif
-
-static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue)
+ smallint chunked; /* chunked transfer encoding */
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+struct BUG_G_too_big {
+ char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
+#define content_len (G.content_len )
+#define beg_range (G.beg_range )
+#define lastsize (G.lastsize )
+#define totalsize (G.totalsize )
+#define transferred (G.transferred )
+#define curfile (G.curfile )
+#define lastupdate_sec (G.lastupdate_sec )
+#define start_sec (G.start_sec )
+#define chunked (G.chunked )
+#define INIT_G() do { } while (0)
+
+
+#if ENABLE_FEATURE_WGET_STATUSBAR
+enum {
+ STALLTIME = 5 /* Seconds when xfer considered "stalled" */
+};
+
+static unsigned int get_tty2_width(void)
{
- if (output != stdout && do_continue==0) {
- fclose(output);
- unlink(fname_out);
- }
+ unsigned width;
+ get_terminal_width_height(2, &width, NULL);
+ return width;
}
-/* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the
- * number of elements read, and a short count if an eof or non-interrupt
- * error is encountered. */
-static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+static void progress_meter(int flag)
{
- size_t ret = 0;
+ /* We can be called from signal handler */
+ int save_errno = errno;
+ off_t abbrevsize;
+ unsigned since_last_update, elapsed;
+ unsigned ratio;
+ int barlength, i;
+
+ if (flag == -1) { /* first call to progress_meter */
+ start_sec = monotonic_sec();
+ lastupdate_sec = start_sec;
+ lastsize = 0;
+ totalsize = content_len + beg_range; /* as content_len changes.. */
+ }
- do {
- clearerr(stream);
- ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
- } while (ret < nmemb && ferror(stream) && errno == EINTR);
+ ratio = 100;
+ if (totalsize != 0 && !chunked) {
+ /* long long helps to have it working even if !LFS */
+ ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
+ if (ratio > 100) ratio = 100;
+ }
- return ret;
+ fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
+
+ barlength = get_tty2_width() - 49;
+ if (barlength > 0) {
+ /* god bless gcc for variable arrays :) */
+ i = barlength * ratio / 100;
+ {
+ char buf[i+1];
+ memset(buf, '*', i);
+ buf[i] = '\0';
+ fprintf(stderr, "|%s%*s|", buf, barlength - i, "");
+ }
+ }
+ i = 0;
+ abbrevsize = transferred + beg_range;
+ while (abbrevsize >= 100000) {
+ i++;
+ abbrevsize >>= 10;
+ }
+ /* see http://en.wikipedia.org/wiki/Tera */
+ fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]);
+
+// Nuts! Ain't it easier to update progress meter ONLY when we transferred++?
+
+ elapsed = monotonic_sec();
+ since_last_update = elapsed - lastupdate_sec;
+ if (transferred > lastsize) {
+ lastupdate_sec = elapsed;
+ lastsize = transferred;
+ if (since_last_update >= STALLTIME) {
+ /* We "cut off" these seconds from elapsed time
+ * by adjusting start time */
+ start_sec += since_last_update;
+ }
+ since_last_update = 0; /* we are un-stalled now */
+ }
+ elapsed -= start_sec; /* now it's "elapsed since start" */
+
+ if (since_last_update >= STALLTIME) {
+ fprintf(stderr, " - stalled -");
+ } else {
+ off_t to_download = totalsize - beg_range;
+ if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) {
+ fprintf(stderr, "--:--:-- ETA");
+ } else {
+ /* to_download / (transferred/elapsed) - elapsed: */
+ int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
+ /* (long long helps to have working ETA even if !LFS) */
+ i = eta % 3600;
+ fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
+ }
+ }
+
+ if (flag == 0) {
+ /* last call to progress_meter */
+ alarm(0);
+ transferred = 0;
+ fputc('\n', stderr);
+ } else {
+ if (flag == -1) { /* first call to progress_meter */
+ signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
+ }
+ alarm(1);
+ }
+
+ errno = save_errno;
}
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
+ * much of which was blatantly stolen from openssh. */
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. 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.
+ *
+ */
+#else /* FEATURE_WGET_STATUSBAR */
+
+static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
-/* Write NMEMB elements of SIZE bytes from PTR to STREAM. Returns the
- * number of elements written, and a short count if an eof or non-interrupt
- * error is encountered. */
-static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
+#endif
+
+
+/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
+ * and a short count if an eof or non-interrupt error is encountered. */
+static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
{
- size_t ret = 0;
+ size_t ret;
+ char *p = (char*)ptr;
do {
clearerr(stream);
- ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
- } while (ret < nmemb && ferror(stream) && errno == EINTR);
+ errno = 0;
+ ret = fread(p, 1, nmemb, stream);
+ p += ret;
+ nmemb -= ret;
+ } while (nmemb && ferror(stream) && errno == EINTR);
- return ret;
+ return p - (char*)ptr;
}
-/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.
+/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
* Returns S, or NULL if an eof or non-interrupt error is encountered. */
static char *safe_fgets(char *s, int size, FILE *stream)
{
@@ -102,727 +220,676 @@ static char *safe_fgets(char *s, int size, FILE *stream)
do {
clearerr(stream);
+ errno = 0;
ret = fgets(s, size, stream);
} while (ret == NULL && ferror(stream) && errno == EINTR);
return ret;
}
-#define close_delete_and_die(s...) { \
- close_and_delete_outfile(output, fname_out, do_continue); \
- bb_error_msg_and_die(s); }
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+/* Base64-encode character string. buf is assumed to be char buf[512]. */
+static char *base64enc_512(char buf[512], const char *str)
+{
+ unsigned len = strlen(str);
+ if (len > 512/4*3 - 10) /* paranoia */
+ len = 512/4*3 - 10;
+ bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
+ return buf;
+}
+#endif
+
+static FILE *open_socket(len_and_sockaddr *lsa)
+{
+ FILE *fp;
-#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
-/*
- * Base64-encode character string
- * oops... isn't something similar in uuencode.c?
- * It would be better to use already existing code
- */
-char *base64enc(char *p, char *buf, int len) {
-
- char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
- char *s = buf;
-
- while(*p) {
- if (s >= buf+len-4)
- bb_error_msg_and_die("buffer overflow");
- *(s++) = al[(*p >> 2) & 0x3F];
- *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
- *s = *(s+1) = '=';
- *(s+2) = 0;
- if (! *(++p)) break;
- *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
- if (! *(++p)) break;
- *(s++) = al[*(p++) & 0x3F];
- }
-
- return buf;
+ /* glibc 2.4 seems to try seeking on it - ??! */
+ /* hopefully it understands what ESPIPE means... */
+ fp = fdopen(xconnect_stream(lsa), "r+");
+ if (fp == NULL)
+ bb_perror_msg_and_die("fdopen");
+
+ return fp;
+}
+
+
+static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
+{
+ int result;
+ if (s1) {
+ if (!s2) s2 = "";
+ fprintf(fp, "%s%s\r\n", s1, s2);
+ fflush(fp);
+ }
+
+ do {
+ char *buf_ptr;
+
+ if (fgets(buf, 510, fp) == NULL) {
+ bb_perror_msg_and_die("error getting response");
+ }
+ buf_ptr = strstr(buf, "\r\n");
+ if (buf_ptr) {
+ *buf_ptr = '\0';
+ }
+ } while (!isdigit(buf[0]) || buf[3] != ' ');
+
+ buf[3] = '\0';
+ result = xatoi_u(buf);
+ buf[3] = ' ';
+ return result;
+}
+
+
+static void parse_url(char *src_url, struct host_info *h)
+{
+ char *url, *p, *sp;
+
+ /* h->allocated = */ url = xstrdup(src_url);
+
+ if (strncmp(url, "http://", 7) == 0) {
+ h->port = bb_lookup_port("http", "tcp", 80);
+ h->host = url + 7;
+ h->is_ftp = 0;
+ } else if (strncmp(url, "ftp://", 6) == 0) {
+ h->port = bb_lookup_port("ftp", "tcp", 21);
+ h->host = url + 6;
+ h->is_ftp = 1;
+ } else
+ bb_error_msg_and_die("not an http or ftp url: %s", url);
+
+ // FYI:
+ // "Real" wget 'http://busybox.net?var=a/b' sends this request:
+ // 'GET /?var=a/b HTTP 1.0'
+ // and saves 'index.html?var=a%2Fb' (we save 'b')
+ // wget 'http://busybox.net?login=john@doe':
+ // request: 'GET /?login=john@doe HTTP/1.0'
+ // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
+ // wget 'http://busybox.net#test/test':
+ // request: 'GET / HTTP/1.0'
+ // saves: 'index.html' (we save 'test')
+ //
+ // We also don't add unique .N suffix if file exists...
+ sp = strchr(h->host, '/');
+ p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
+ p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
+ if (!sp) {
+ h->path = "";
+ } else if (*sp == '/') {
+ *sp = '\0';
+ h->path = sp + 1;
+ } else { // '#' or '?'
+ // http://busybox.net?login=john@doe is a valid URL
+ // memmove converts to:
+ // http:/busybox.nett?login=john@doe...
+ memmove(h->host - 1, h->host, sp - h->host);
+ h->host--;
+ sp[-1] = '\0';
+ h->path = sp;
+ }
+
+ sp = strrchr(h->host, '@');
+ h->user = NULL;
+ if (sp != NULL) {
+ h->user = h->host;
+ *sp = '\0';
+ h->host = sp + 1;
+ }
+
+ sp = h->host;
+}
+
+
+static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
+{
+ char *s, *hdrval;
+ int c;
+
+ /* *istrunc = 0; */
+
+ /* retrieve header line */
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+
+ /* see if we are at the end of the headers */
+ for (s = buf; *s == '\r'; ++s)
+ continue;
+ if (*s == '\n')
+ return NULL;
+
+ /* convert the header name to lower case */
+ for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
+ *s = tolower(*s);
+
+ /* verify we are at the end of the header name */
+ if (*s != ':')
+ bb_error_msg_and_die("bad header line: %s", buf);
+
+ /* locate the start of the header value */
+ *s++ = '\0';
+ hdrval = skip_whitespace(s);
+
+ /* locate the end of header */
+ while (*s && *s != '\r' && *s != '\n')
+ ++s;
+
+ /* end of header found */
+ if (*s) {
+ *s = '\0';
+ return hdrval;
+ }
+
+ /* Rats! The buffer isn't big enough to hold the entire header value. */
+ while (c = getc(fp), c != EOF && c != '\n')
+ continue;
+ /* *istrunc = 1; */
+ return hdrval;
+}
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+static char *URL_escape(const char *str)
+{
+ /* URL encode, see RFC 2396 */
+ char *dst;
+ char *res = dst = xmalloc(strlen(str) * 3 + 1);
+ unsigned char c;
+
+ while (1) {
+ c = *str++;
+ if (c == '\0'
+ /* || strchr("!&'()*-.=_~", c) - more code */
+ || c == '!'
+ || c == '&'
+ || c == '\''
+ || c == '('
+ || c == ')'
+ || c == '*'
+ || c == '-'
+ || c == '.'
+ || c == '='
+ || c == '_'
+ || c == '~'
+ || (c >= '0' && c <= '9')
+ || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
+ ) {
+ *dst++ = c;
+ if (c == '\0')
+ return res;
+ } else {
+ *dst++ = '%';
+ *dst++ = bb_hexdigits_upcase[c >> 4];
+ *dst++ = bb_hexdigits_upcase[c & 0xf];
+ }
+ }
}
#endif
-int wget_main(int argc, char **argv)
+int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int wget_main(int argc UNUSED_PARAM, char **argv)
{
- int n, try=5, status;
+ char buf[512];
+ struct host_info server, target;
+ len_and_sockaddr *lsa;
+ int status;
int port;
+ int try = 5;
+ unsigned opt;
+ char *str;
char *proxy = 0;
- char *dir_prefix=NULL;
- char *s, buf[512];
- struct stat sbuf;
- char extra_headers[1024];
- char *extra_headers_ptr = extra_headers;
- int extra_headers_left = sizeof(extra_headers);
- int which_long_opt = 0, option_index = -1;
- struct host_info server, target;
-
- FILE *sfp = NULL; /* socket to web/ftp server */
- FILE *dfp = NULL; /* socket to ftp server (data) */
- char *fname_out = NULL; /* where to direct output (-O) */
- int do_continue = 0; /* continue a prev transfer (-c) */
- long beg_range = 0L; /* range at which continue begins */
- int got_clen = 0; /* got content-length: from server */
- FILE *output; /* socket to web server */
- int quiet_flag = FALSE; /* Be verry, verry quiet... */
- int noproxy = 0; /* Use proxies if env vars are set */
-
-#define LONG_HEADER 1
-#define LONG_PASSIVE 2
-
- struct option long_options[] = {
- { "continue", 0, NULL, 'c' },
- { "quiet", 0, NULL, 'q' },
- { "output-document", 1, NULL, 'O' },
- { "header", 1, &which_long_opt, LONG_HEADER },
- { "proxy", 1, NULL, 'Y' },
- { "passive-ftp", 0, &which_long_opt, LONG_PASSIVE },
- { 0, 0, 0, 0 }
+ char *dir_prefix = NULL;
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ char *post_data;
+ char *extra_headers = NULL;
+ llist_t *headers_llist = NULL;
+#endif
+ FILE *sfp = NULL; /* socket to web/ftp server */
+ FILE *dfp; /* socket to ftp server (data) */
+ char *fname_out; /* where to direct output (-O) */
+ bool got_clen = 0; /* got content-length: from server */
+ int output_fd = -1;
+ bool use_proxy = 1; /* Use proxies if env vars are set */
+ const char *proxy_flag = "on"; /* Use proxies if env vars are set */
+ const char *user_agent = "Wget";/* "User-Agent" header field */
+
+ static const char keywords[] ALIGN1 =
+ "content-length\0""transfer-encoding\0""chunked\0""location\0";
+ enum {
+ KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
};
- /*
- * Crack command line.
- */
- while ((n = getopt_long(argc, argv, "cqO:P:Y:", long_options, &option_index)) != EOF) {
- switch (n) {
- case 'c':
- ++do_continue;
- break;
- case 'P':
- dir_prefix = optarg;
- break;
- case 'q':
- quiet_flag = TRUE;
- break;
- case 'O':
- /* can't set fname_out to NULL if outputting to stdout, because
- * this gets interpreted as the auto-gen output filename
- * case below - tausq@debian.org
- */
- fname_out = optarg;
- break;
- case 'Y':
- if (strcmp(optarg, "off") == 0)
- noproxy=1;
- break;
- case 0:
- switch (which_long_opt) {
- case LONG_HEADER: {
- int arglen = strlen(optarg);
- if(extra_headers_left - arglen - 2 <= 0)
- bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen);
- strcpy(extra_headers_ptr, optarg);
- extra_headers_ptr += arglen;
- extra_headers_left -= ( arglen + 2 );
- *extra_headers_ptr++ = '\r';
- *extra_headers_ptr++ = '\n';
- *(extra_headers_ptr + 1) = 0;
- break;
- }
- case LONG_PASSIVE:
- // ignore -- we always use passive mode
- break;
- }
- break;
- default:
- bb_show_usage();
+ enum {
+ WGET_OPT_CONTINUE = (1 << 0),
+ WGET_OPT_SPIDER = (1 << 1),
+ WGET_OPT_QUIET = (1 << 2),
+ WGET_OPT_OUTNAME = (1 << 3),
+ WGET_OPT_PREFIX = (1 << 4),
+ WGET_OPT_PROXY = (1 << 5),
+ WGET_OPT_USER_AGENT = (1 << 6),
+ WGET_OPT_RETRIES = (1 << 7),
+ WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
+ WGET_OPT_PASSIVE = (1 << 9),
+ WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+ WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+ };
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ static const char wget_longopts[] ALIGN1 =
+ /* name, has_arg, val */
+ "continue\0" No_argument "c"
+ "spider\0" No_argument "s"
+ "quiet\0" No_argument "q"
+ "output-document\0" Required_argument "O"
+ "directory-prefix\0" Required_argument "P"
+ "proxy\0" Required_argument "Y"
+ "user-agent\0" Required_argument "U"
+ /* Ignored: */
+ // "tries\0" Required_argument "t"
+ // "timeout\0" Required_argument "T"
+ /* Ignored (we always use PASV): */
+ "passive-ftp\0" No_argument "\xff"
+ "header\0" Required_argument "\xfe"
+ "post-data\0" Required_argument "\xfd"
+ ;
+#endif
+
+ INIT_G();
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ applet_long_options = wget_longopts;
+#endif
+ /* server.allocated = target.allocated = NULL; */
+ opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
+ opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
+ &fname_out, &dir_prefix,
+ &proxy_flag, &user_agent,
+ NULL, /* -t RETRIES */
+ NULL /* -T NETWORK_READ_TIMEOUT */
+ USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
+ USE_FEATURE_WGET_LONG_OPTIONS(, &post_data)
+ );
+ if (strcmp(proxy_flag, "off") == 0) {
+ /* Use the proxy if necessary */
+ use_proxy = 0;
+ }
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (headers_llist) {
+ int size = 1;
+ char *cp;
+ llist_t *ll = headers_llist;
+ while (ll) {
+ size += strlen(ll->data) + 2;
+ ll = ll->link;
+ }
+ extra_headers = cp = xmalloc(size);
+ while (headers_llist) {
+ cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
}
}
-
- if (argc - optind != 1)
- bb_show_usage();
+#endif
parse_url(argv[optind], &target);
server.host = target.host;
server.port = target.port;
- /*
- * Use the proxy if necessary.
- */
- if (!noproxy) {
+ /* Use the proxy if necessary */
+ if (use_proxy) {
proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
- if (proxy)
- parse_url(bb_xstrdup(proxy), &server);
- }
-
- /* Guess an output filename */
- if (!fname_out) {
- fname_out =
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
- curfile =
-#endif
- bb_get_last_path_component(target.path);
- if (fname_out==NULL || strlen(fname_out)<1) {
- fname_out =
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
- curfile =
-#endif
- "index.html";
+ if (proxy && *proxy) {
+ parse_url(proxy, &server);
+ } else {
+ use_proxy = 0;
}
- if (dir_prefix != NULL)
+ }
+
+ /* Guess an output filename, if there was no -O FILE */
+ if (!(opt & WGET_OPT_OUTNAME)) {
+ fname_out = bb_get_last_path_component_nostrip(target.path);
+ /* handle "wget http://kernel.org//" */
+ if (fname_out[0] == '/' || !fname_out[0])
+ fname_out = (char*)"index.html";
+ /* -P DIR is considered only if there was no -O FILE */
+ if (dir_prefix)
fname_out = concat_path_file(dir_prefix, fname_out);
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
} else {
- curfile = bb_get_last_path_component(fname_out);
-#endif
+ if (LONE_DASH(fname_out)) {
+ /* -O - */
+ output_fd = 1;
+ opt &= ~WGET_OPT_CONTINUE;
+ }
}
- if (do_continue && !fname_out)
- bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ curfile = bb_get_last_path_component_nostrip(fname_out);
+#endif
+ /* Impossible?
+ if ((opt & WGET_OPT_CONTINUE) && !fname_out)
+ bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
- /*
- * Open the output file stream.
- */
- if (strcmp(fname_out, "-") == 0) {
- output = stdout;
- quiet_flag = TRUE;
- } else {
- output = bb_xfopen(fname_out, (do_continue ? "a" : "w"));
+ /* Determine where to start transfer */
+ if (opt & WGET_OPT_CONTINUE) {
+ output_fd = open(fname_out, O_WRONLY);
+ if (output_fd >= 0) {
+ beg_range = xlseek(output_fd, 0, SEEK_END);
+ }
+ /* File doesn't exist. We do not create file here yet.
+ We are not sure it exists on remove side */
}
- /*
- * Determine where to start transfer.
- */
- if (do_continue) {
- if (fstat(fileno(output), &sbuf) < 0)
- bb_perror_msg_and_die("fstat()");
- if (sbuf.st_size > 0)
- beg_range = sbuf.st_size;
- else
- do_continue = 0;
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ lsa = xhost2sockaddr(server.host, server.port);
+ if (!(opt & WGET_OPT_QUIET)) {
+ fprintf(stderr, "Connecting to %s (%s)\n", server.host,
+ xmalloc_sockaddr2dotted(&lsa->u.sa));
+ /* We leak result of xmalloc_sockaddr2dotted */
}
- if (proxy || !target.is_ftp) {
+ if (use_proxy || !target.is_ftp) {
/*
* HTTP session
*/
do {
- if (! --try)
- close_delete_and_die("too many redirections");
+ got_clen = 0;
+ chunked = 0;
- /*
- * Open socket to http server
- */
+ if (!--try)
+ bb_error_msg_and_die("too many redirections");
+
+ /* Open socket to http server */
if (sfp) fclose(sfp);
- sfp = open_socket(server.host, server.port);
-
- /*
- * Send HTTP request.
- */
- if (proxy) {
- fprintf(sfp, "GET %stp://%s:%d/%s HTTP/1.1\r\n",
+ sfp = open_socket(lsa);
+
+ /* Send HTTP request. */
+ if (use_proxy) {
+ fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
target.is_ftp ? "f" : "ht", target.host,
- target.port, target.path);
+ target.path);
} else {
- fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+ if (opt & WGET_OPT_POST_DATA)
+ fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
+ else
+ fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
}
- fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host);
+ fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
+ target.host, user_agent);
-#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
if (target.user) {
- fprintf(sfp, "Authorization: Basic %s\r\n",
- base64enc(target.user, buf, sizeof(buf)));
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
+ base64enc_512(buf, target.user));
}
- if (proxy && server.user) {
+ if (use_proxy && server.user) {
fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
- base64enc(server.user, buf, sizeof(buf)));
+ base64enc_512(buf, server.user));
}
#endif
- if (do_continue)
- fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range);
- if(extra_headers_left < sizeof(extra_headers))
- fputs(extra_headers,sfp);
- fprintf(sfp,"Connection: close\r\n\r\n");
+ if (beg_range)
+ fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (extra_headers)
+ fputs(extra_headers, sfp);
+
+ if (opt & WGET_OPT_POST_DATA) {
+ char *estr = URL_escape(post_data);
+ fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
+ fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
+ (int) strlen(estr), estr);
+ /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
+ /*fprintf(sfp, "%s\r\n", estr);*/
+ free(estr);
+ } else
+#endif
+ { /* If "Connection:" is needed, document why */
+ fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
+ }
/*
- * Retrieve HTTP response line and check for "200" status code.
- */
-read_response: if (fgets(buf, sizeof(buf), sfp) == NULL)
- close_delete_and_die("no response from server");
-
- for (s = buf ; *s != '\0' && !isspace(*s) ; ++s)
- ;
- for ( ; isspace(*s) ; ++s)
- ;
- switch (status = atoi(s)) {
- case 0:
- case 100:
- while (gethdr(buf, sizeof(buf), sfp, &n) != NULL);
- goto read_response;
- case 200:
- if (do_continue && output != stdout)
- output = freopen(fname_out, "w", output);
- do_continue = 0;
- break;
- case 300: /* redirection */
- case 301:
- case 302:
- case 303:
+ * Retrieve HTTP response line and check for "200" status code.
+ */
+ read_response:
+ if (fgets(buf, sizeof(buf), sfp) == NULL)
+ bb_error_msg_and_die("no response from server");
+
+ str = buf;
+ str = skip_non_whitespace(str);
+ str = skip_whitespace(str);
+ // FIXME: no error check
+ // xatou wouldn't work: "200 OK"
+ status = atoi(str);
+ switch (status) {
+ case 0:
+ case 100:
+ while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
+ /* eat all remaining headers */;
+ goto read_response;
+ case 200:
+/*
+Response 204 doesn't say "null file", it says "metadata
+has changed but data didn't":
+
+"10.2.5 204 No Content
+The server has fulfilled the request but does not need to return
+an entity-body, and might want to return updated metainformation.
+The response MAY include new or updated metainformation in the form
+of entity-headers, which if present SHOULD be associated with
+the requested variant.
+
+If the client is a user agent, it SHOULD NOT change its document
+view from that which caused the request to be sent. This response
+is primarily intended to allow input for actions to take place
+without causing a change to the user agent's active document view,
+although any new or updated metainformation SHOULD be applied
+to the document currently in the user agent's active view.
+
+The 204 response MUST NOT include a message-body, and thus
+is always terminated by the first empty line after the header fields."
+
+However, in real world it was observed that some web servers
+(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
+*/
+ case 204:
+ break;
+ case 300: /* redirection */
+ case 301:
+ case 302:
+ case 303:
+ break;
+ case 206:
+ if (beg_range)
break;
- case 206:
- if (do_continue)
- break;
- /*FALLTHRU*/
- default:
- chomp(buf);
- close_delete_and_die("server returned error %d: %s", atoi(s), buf);
+ /* fall through */
+ default:
+ /* Show first line only and kill any ESC tricks */
+ buf[strcspn(buf, "\n\r\x1b")] = '\0';
+ bb_error_msg_and_die("server returned error: %s", buf);
}
-
+
/*
* Retrieve HTTP headers.
*/
- while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
- if (strcasecmp(buf, "content-length") == 0) {
- filesize = atol(s);
+ while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
+ /* gethdr did already convert the "FOO:" string to lowercase */
+ smalluint key = index_in_strings(keywords, *&buf) + 1;
+ if (key == KEY_content_length) {
+ content_len = BB_STRTOOFF(str, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("content-length %s is garbage", str);
+ }
got_clen = 1;
continue;
}
- if (strcasecmp(buf, "transfer-encoding") == 0) {
- if (strcasecmp(s, "chunked") == 0) {
- chunked = got_clen = 1;
- } else {
- close_delete_and_die("server wants to do %s transfer encoding", s);
- }
+ if (key == KEY_transfer_encoding) {
+ if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
+ bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
+ chunked = got_clen = 1;
}
- if (strcasecmp(buf, "location") == 0) {
- if (s[0] == '/')
- target.path = bb_xstrdup(s+1);
+ if (key == KEY_location) {
+ if (str[0] == '/')
+ /* free(target.allocated); */
+ target.path = /* target.allocated = */ xstrdup(str+1);
else {
- parse_url(bb_xstrdup(s), &target);
- if (!proxy) {
+ parse_url(str, &target);
+ if (use_proxy == 0) {
server.host = target.host;
server.port = target.port;
}
+ free(lsa);
+ lsa = xhost2sockaddr(server.host, server.port);
+ break;
}
}
}
- } while(status >= 300);
-
+ } while (status >= 300);
+
dfp = sfp;
- }
- else
- {
+
+ } else {
+
/*
* FTP session
*/
- if (! target.user)
- target.user = bb_xstrdup("anonymous:busybox@");
+ if (!target.user)
+ target.user = xstrdup("anonymous:busybox@");
- sfp = open_socket(server.host, server.port);
+ sfp = open_socket(lsa);
if (ftpcmd(NULL, NULL, sfp, buf) != 220)
- close_delete_and_die("%s", buf+4);
+ bb_error_msg_and_die("%s", buf+4);
- /*
+ /*
* Splitting username:password pair,
* trying to log in
*/
- s = strchr(target.user, ':');
- if (s)
- *(s++) = '\0';
- switch(ftpcmd("USER ", target.user, sfp, buf)) {
- case 230:
+ str = strchr(target.user, ':');
+ if (str)
+ *(str++) = '\0';
+ switch (ftpcmd("USER ", target.user, sfp, buf)) {
+ case 230:
+ break;
+ case 331:
+ if (ftpcmd("PASS ", str, sfp, buf) == 230)
break;
- case 331:
- if (ftpcmd("PASS ", s, sfp, buf) == 230)
- break;
- /* FALLTHRU (failed login) */
- default:
- close_delete_and_die("ftp login: %s", buf+4);
+ /* fall through (failed login) */
+ default:
+ bb_error_msg_and_die("ftp login: %s", buf+4);
}
-
- ftpcmd("CDUP", NULL, sfp, buf);
+
ftpcmd("TYPE I", NULL, sfp, buf);
-
+
/*
* Querying file size
*/
- if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) {
- filesize = atol(buf+4);
+ if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
+ content_len = BB_STRTOOFF(buf+4, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("SIZE value is garbage");
+ }
got_clen = 1;
}
-
+
/*
* Entering passive mode
*/
- if (ftpcmd("PASV", NULL, sfp, buf) != 227)
- close_delete_and_die("PASV: %s", buf+4);
- s = strrchr(buf, ',');
- *s = 0;
- port = atoi(s+1);
- s = strrchr(buf, ',');
- port += atoi(s+1) * 256;
- dfp = open_socket(server.host, port);
-
- if (do_continue) {
- sprintf(buf, "REST %ld", beg_range);
- if (ftpcmd(buf, NULL, sfp, buf) != 350) {
- if (output != stdout)
- output = freopen(fname_out, "w", output);
- do_continue = 0;
- } else
- filesize -= beg_range;
+ if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
+ pasv_error:
+ bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
+ }
+ // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
+ // Server's IP is N1.N2.N3.N4 (we ignore it)
+ // Server's port for data connection is P1*256+P2
+ str = strrchr(buf, ')');
+ if (str) str[0] = '\0';
+ str = strrchr(buf, ',');
+ if (!str) goto pasv_error;
+ port = xatou_range(str+1, 0, 255);
+ *str = '\0';
+ str = strrchr(buf, ',');
+ if (!str) goto pasv_error;
+ port += xatou_range(str+1, 0, 255) * 256;
+ set_nport(lsa, htons(port));
+ dfp = open_socket(lsa);
+
+ if (beg_range) {
+ sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+ if (ftpcmd(buf, NULL, sfp, buf) == 350)
+ content_len -= beg_range;
}
-
- if (ftpcmd("RETR /", target.path, sfp, buf) > 150)
- close_delete_and_die("RETR: %s", buf+4);
+ if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
+ bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
}
+ if (opt & WGET_OPT_SPIDER) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(sfp);
+ return EXIT_SUCCESS;
+ }
/*
* Retrieve file
*/
- if (chunked) {
- fgets(buf, sizeof(buf), dfp);
- filesize = strtol(buf, (char **) NULL, 16);
+
+ /* Do it before progress_meter (want to have nice error message) */
+ if (output_fd < 0) {
+ int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
+ /* compat with wget: -O FILE can overwrite */
+ if (opt & WGET_OPT_OUTNAME)
+ o_flags = O_WRONLY | O_CREAT | O_TRUNC;
+ output_fd = xopen(fname_out, o_flags);
}
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
- if (quiet_flag==FALSE)
- progressmeter(-1);
-#endif
- do {
- while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) {
- if (safe_fwrite(buf, 1, n, output) != n)
- bb_perror_msg_and_die("write error");
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
- statbytes+=n;
+
+ if (!(opt & WGET_OPT_QUIET))
+ progress_meter(-1);
+
+ if (chunked)
+ goto get_clen;
+
+ /* Loops only if chunked */
+ while (1) {
+ while (content_len > 0 || !got_clen) {
+ int n;
+ unsigned rdsz = sizeof(buf);
+
+ if (content_len < sizeof(buf) && (chunked || got_clen))
+ rdsz = (unsigned)content_len;
+ n = safe_fread(buf, rdsz, dfp);
+ if (n <= 0) {
+ if (ferror(dfp)) {
+ /* perror will not work: ferror doesn't set errno */
+ bb_error_msg_and_die(bb_msg_read_error);
+ }
+ break;
+ }
+ xwrite(output_fd, buf, n);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ transferred += n;
#endif
- if (got_clen)
- filesize -= n;
+ if (got_clen)
+ content_len -= n;
+ }
+
+ if (!chunked)
+ break;
+
+ safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+ get_clen:
+ safe_fgets(buf, sizeof(buf), dfp);
+ content_len = STRTOOFF(buf, NULL, 16);
+ /* FIXME: error check? */
+ if (content_len == 0)
+ break; /* all done! */
}
- if (chunked) {
- safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
- safe_fgets(buf, sizeof(buf), dfp);
- filesize = strtol(buf, (char **) NULL, 16);
- if (filesize==0) chunked = 0; /* all done! */
- }
+ if (!(opt & WGET_OPT_QUIET))
+ progress_meter(0);
- if (n == 0 && ferror(dfp))
- bb_perror_msg_and_die("network read error");
- } while (chunked);
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
- if (quiet_flag==FALSE)
- progressmeter(1);
-#endif
- if (!proxy && target.is_ftp) {
+ if ((use_proxy == 0) && target.is_ftp) {
fclose(dfp);
if (ftpcmd(NULL, NULL, sfp, buf) != 226)
bb_error_msg_and_die("ftp error: %s", buf+4);
ftpcmd("QUIT", NULL, sfp, buf);
}
- exit(EXIT_SUCCESS);
-}
-
-
-void parse_url(char *url, struct host_info *h)
-{
- char *cp, *sp, *up;
-
- if (strncmp(url, "http://", 7) == 0) {
- h->port = 80;
- h->host = url + 7;
- h->is_ftp = 0;
- } else if (strncmp(url, "ftp://", 6) == 0) {
- h->port = 21;
- h->host = url + 6;
- h->is_ftp = 1;
- } else
- bb_error_msg_and_die("not an http or ftp url: %s", url);
-
- sp = strchr(h->host, '/');
- if (sp != NULL) {
- *sp++ = '\0';
- h->path = sp;
- } else
- h->path = bb_xstrdup("");
-
- up = strrchr(h->host, '@');
- if (up != NULL) {
- h->user = h->host;
- *up++ = '\0';
- h->host = up;
- } else
- h->user = NULL;
-
- cp = strchr(h->host, ':');
- if (cp != NULL) {
- *cp++ = '\0';
- h->port = atoi(cp);
- }
-
-}
-
-
-FILE *open_socket(char *host, int port)
-{
- int fd;
- FILE *fp;
- char port_str[10];
-
- snprintf(port_str, sizeof(port_str), "%d", port);
- fd=xconnect(host, port_str);
-
- /*
- * Get the server onto a stdio stream.
- */
- if ((fp = fdopen(fd, "r+")) == NULL)
- bb_perror_msg_and_die("fdopen()");
-
- return fp;
-}
-
-
-char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
-{
- char *s, *hdrval;
- int c;
-
- *istrunc = 0;
-
- /* retrieve header line */
- if (fgets(buf, bufsiz, fp) == NULL)
- return NULL;
-
- /* see if we are at the end of the headers */
- for (s = buf ; *s == '\r' ; ++s)
- ;
- if (s[0] == '\n')
- return NULL;
-
- /* convert the header name to lower case */
- for (s = buf ; isalnum(*s) || *s == '-' ; ++s)
- *s = tolower(*s);
-
- /* verify we are at the end of the header name */
- if (*s != ':')
- bb_error_msg_and_die("bad header line: %s", buf);
-
- /* locate the start of the header value */
- for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s)
- ;
- hdrval = s;
-
- /* locate the end of header */
- while (*s != '\0' && *s != '\r' && *s != '\n')
- ++s;
-
- /* end of header found */
- if (*s != '\0') {
- *s = '\0';
- return hdrval;
- }
-
- /* Rats! The buffer isn't big enough to hold the entire header value. */
- while (c = getc(fp), c != EOF && c != '\n')
- ;
- *istrunc = 1;
- return hdrval;
-}
-
-static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)
-{
- char *p;
-
- if (s1) {
- if (!s2) s2="";
- fprintf(fp, "%s%s\r\n", s1, s2);
- fflush(fp);
- }
-
- do {
- p = fgets(buf, 510, fp);
- if (!p)
- bb_perror_msg_and_die("fgets()");
- } while (! isdigit(buf[0]) || buf[3] != ' ');
-
- return atoi(buf);
-}
-
-#ifdef CONFIG_FEATURE_WGET_STATUSBAR
-/* Stuff below is from BSD rcp util.c, as added to openshh.
- * Original copyright notice is retained at the end of this file.
- *
- */
-
-
-static int
-getttywidth(void)
-{
- struct winsize winsize;
-
- if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
- return (winsize.ws_col ? winsize.ws_col : 80);
- else
- return (80);
-}
-
-static void
-updateprogressmeter(int ignore)
-{
- int save_errno = errno;
-
- progressmeter(0);
- errno = save_errno;
-}
-
-static void
-alarmtimer(int wait)
-{
- struct itimerval itv;
-
- itv.it_value.tv_sec = wait;
- itv.it_value.tv_usec = 0;
- itv.it_interval = itv.it_value;
- setitimer(ITIMER_REAL, &itv, NULL);
-}
-
-
-static void
-progressmeter(int flag)
-{
- static const char prefixes[] = " KMGTP";
- static struct timeval lastupdate;
- static off_t lastsize, totalsize;
- struct timeval now, td, wait;
- off_t cursize, abbrevsize;
- double elapsed;
- int ratio, barlength, i, remaining;
- char buf[256];
-
- if (flag == -1) {
- (void) gettimeofday(&start, (struct timezone *) 0);
- lastupdate = start;
- lastsize = 0;
- totalsize = filesize; /* as filesize changes.. */
- }
- (void) gettimeofday(&now, (struct timezone *) 0);
- cursize = statbytes;
- if (totalsize != 0 && !chunked) {
- ratio = 100.0 * cursize / totalsize;
- ratio = MAX(ratio, 0);
- ratio = MIN(ratio, 100);
- } else
- ratio = 100;
-
- snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio);
-
- barlength = getttywidth() - 51;
- if (barlength > 0) {
- i = barlength * ratio / 100;
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "|%.*s%*s|", i,
- "*****************************************************************************"
- "*****************************************************************************",
- barlength - i, "");
- }
- i = 0;
- abbrevsize = cursize;
- while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
- i++;
- abbrevsize >>= 10;
- }
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ",
- (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' :
- 'B');
-
- timersub(&now, &lastupdate, &wait);
- if (cursize > lastsize) {
- lastupdate = now;
- lastsize = cursize;
- if (wait.tv_sec >= STALLTIME) {
- start.tv_sec += wait.tv_sec;
- start.tv_usec += wait.tv_usec;
- }
- wait.tv_sec = 0;
- }
- timersub(&now, &start, &td);
- elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
-
- if (wait.tv_sec >= STALLTIME) {
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- " - stalled -");
- } else if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalsize || chunked) {
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- " --:-- ETA");
- } else {
- remaining = (int) (totalsize / (statbytes / elapsed) - elapsed);
- i = remaining / 3600;
- if (i)
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "%2d:", i);
- else
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- " ");
- i = remaining % 3600;
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "%02d:%02d ETA", i / 60, i % 60);
- }
- write(fileno(stderr), buf, strlen(buf));
-
- if (flag == -1) {
- struct sigaction sa;
- sa.sa_handler = updateprogressmeter;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &sa, NULL);
- alarmtimer(1);
- } else if (flag == 1) {
- alarmtimer(0);
- statbytes = 0;
- putc('\n', stderr);
- }
+ return EXIT_SUCCESS;
}
-#endif
-
-/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
- * much of which was blatently stolen from openssh. */
-
-/*-
- * Copyright (c) 1992, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
- *
- * 4. 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.
- *
- * $Id: wget.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
- */
-
-
-
-/*
-Local Variables:
-c-file-style: "linux"
-c-basic-offset: 4
-tab-width: 4
-End:
-*/
-
-
-
diff --git a/release/src/router/busybox/networking/zcip.c b/release/src/router/busybox/networking/zcip.c
new file mode 100644
index 00000000..df4c0ec2
--- /dev/null
+++ b/release/src/router/busybox/networking/zcip.c
@@ -0,0 +1,567 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * RFC3927 ZeroConf IPv4 Link-Local addressing
+ * (see <http://www.zeroconf.org/>)
+ *
+ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
+ * Copyright (C) 2004 by David Brownell
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * ZCIP just manages the 169.254.*.* addresses. That network is not
+ * routed at the IP level, though various proxies or bridges can
+ * certainly be used. Its naming is built over multicast DNS.
+ */
+
+//#define DEBUG
+
+// TODO:
+// - more real-world usage/testing, especially daemon mode
+// - kernel packet filters to reduce scheduling noise
+// - avoid silent script failures, especially under load...
+// - link status monitoring (restart on link-up; stop on link-down)
+
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/if_packet.h>
+#include <linux/sockios.h>
+
+#include "libbb.h"
+#include <syslog.h>
+
+/* We don't need more than 32 bits of the counter */
+#define MONOTONIC_US() ((unsigned)monotonic_us())
+
+struct arp_packet {
+ struct ether_header eth;
+ struct ether_arp arp;
+} PACKED;
+
+enum {
+/* 169.254.0.0 */
+ LINKLOCAL_ADDR = 0xa9fe0000,
+
+/* protocol timeout parameters, specified in seconds */
+ PROBE_WAIT = 1,
+ PROBE_MIN = 1,
+ PROBE_MAX = 2,
+ PROBE_NUM = 3,
+ MAX_CONFLICTS = 10,
+ RATE_LIMIT_INTERVAL = 60,
+ ANNOUNCE_WAIT = 2,
+ ANNOUNCE_NUM = 2,
+ ANNOUNCE_INTERVAL = 2,
+ DEFEND_INTERVAL = 10
+};
+
+/* States during the configuration process. */
+enum {
+ PROBE = 0,
+ RATE_LIMIT_PROBE,
+ ANNOUNCE,
+ MONITOR,
+ DEFEND
+};
+
+#define VDBG(...) do { } while (0)
+
+
+enum {
+ sock_fd = 3
+};
+
+struct globals {
+ struct sockaddr saddr;
+ struct ether_addr eth_addr;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define saddr (G.saddr )
+#define eth_addr (G.eth_addr)
+
+
+/**
+ * Pick a random link local IP address on 169.254/16, except that
+ * the first and last 256 addresses are reserved.
+ */
+static uint32_t pick(void)
+{
+ unsigned tmp;
+
+ do {
+ tmp = rand() & IN_CLASSB_HOST;
+ } while (tmp > (IN_CLASSB_HOST - 0x0200));
+ return htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
+}
+
+/**
+ * Broadcast an ARP packet.
+ */
+static void arp(
+ /* int op, - always ARPOP_REQUEST */
+ /* const struct ether_addr *source_eth, - always &eth_addr */
+ struct in_addr source_ip,
+ const struct ether_addr *target_eth, struct in_addr target_ip)
+{
+ enum { op = ARPOP_REQUEST };
+#define source_eth (&eth_addr)
+
+ struct arp_packet p;
+ memset(&p, 0, sizeof(p));
+
+ // ether header
+ p.eth.ether_type = htons(ETHERTYPE_ARP);
+ memcpy(p.eth.ether_shost, source_eth, ETH_ALEN);
+ memset(p.eth.ether_dhost, 0xff, ETH_ALEN);
+
+ // arp request
+ p.arp.arp_hrd = htons(ARPHRD_ETHER);
+ p.arp.arp_pro = htons(ETHERTYPE_IP);
+ p.arp.arp_hln = ETH_ALEN;
+ p.arp.arp_pln = 4;
+ p.arp.arp_op = htons(op);
+ memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN);
+ memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa));
+ memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN);
+ memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa));
+
+ // send it
+ // Even though sock_fd is already bound to saddr, just send()
+ // won't work, because "socket is not connected"
+ // (and connect() won't fix that, "operation not supported").
+ // Thus we sendto() to saddr. I wonder which sockaddr
+ // (from bind() or from sendto()?) kernel actually uses
+ // to determine iface to emit the packet from...
+ xsendto(sock_fd, &p, sizeof(p), &saddr, sizeof(saddr));
+#undef source_eth
+}
+
+/**
+ * Run a script.
+ * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL
+ */
+static int run(char *argv[3], const char *param, struct in_addr *ip)
+{
+ int status;
+ char *addr = addr; /* for gcc */
+ const char *fmt = "%s %s %s" + 3;
+
+ argv[2] = (char*)param;
+
+ VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]);
+
+ if (ip) {
+ addr = inet_ntoa(*ip);
+ xsetenv("ip", addr);
+ fmt -= 3;
+ }
+ bb_info_msg(fmt, argv[2], argv[0], addr);
+
+ status = wait4pid(spawn(argv + 1));
+ if (status < 0) {
+ bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]);
+ return -errno;
+ }
+ if (status != 0)
+ bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status);
+ return status;
+}
+
+/**
+ * Return milliseconds of random delay, up to "secs" seconds.
+ */
+static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs)
+{
+ return rand() % (secs * 1000);
+}
+
+/**
+ * main program
+ */
+int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int zcip_main(int argc, char **argv)
+{
+ int state;
+ char *r_opt;
+ unsigned opts;
+
+ // ugly trick, but I want these zeroed in one go
+ struct {
+ const struct in_addr null_ip;
+ const struct ether_addr null_addr;
+ struct in_addr ip;
+ struct ifreq ifr;
+ int timeout_ms; /* must be signed */
+ unsigned conflicts;
+ unsigned nprobes;
+ unsigned nclaims;
+ int ready;
+ int verbose;
+ } L;
+#define null_ip (L.null_ip )
+#define null_addr (L.null_addr )
+#define ip (L.ip )
+#define ifr (L.ifr )
+#define timeout_ms (L.timeout_ms)
+#define conflicts (L.conflicts )
+#define nprobes (L.nprobes )
+#define nclaims (L.nclaims )
+#define ready (L.ready )
+#define verbose (L.verbose )
+
+ memset(&L, 0, sizeof(L));
+
+#define FOREGROUND (opts & 1)
+#define QUIT (opts & 2)
+ // parse commandline: prog [options] ifname script
+ // exactly 2 args; -v accumulates and implies -f
+ opt_complementary = "=2:vv:vf";
+ opts = getopt32(argv, "fqr:v", &r_opt, &verbose);
+#if !BB_MMU
+ // on NOMMU reexec early (or else we will rerun things twice)
+ if (!FOREGROUND)
+ bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv);
+#endif
+ // open an ARP socket
+ // (need to do it before openlog to prevent openlog from taking
+ // fd 3 (sock_fd==3))
+ xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd);
+ if (!FOREGROUND) {
+ // do it before all bb_xx_msg calls
+ openlog(applet_name, 0, LOG_DAEMON);
+ logmode |= LOGMODE_SYSLOG;
+ }
+ if (opts & 4) { // -r n.n.n.n
+ if (inet_aton(r_opt, &ip) == 0
+ || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
+ ) {
+ bb_error_msg_and_die("invalid link address");
+ }
+ }
+ argc -= optind;
+ argv += optind - 1;
+
+ /* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */
+ /* We need to make space for script argument: */
+ argv[0] = argv[1];
+ argv[1] = argv[2];
+ /* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */
+#define argv_intf (argv[0])
+
+ xsetenv("interface", argv_intf);
+
+ // initialize the interface (modprobe, ifup, etc)
+ if (run(argv, "init", NULL))
+ return EXIT_FAILURE;
+
+ // initialize saddr
+ // saddr is: { u16 sa_family; u8 sa_data[14]; }
+ //memset(&saddr, 0, sizeof(saddr));
+ //TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
+ safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data));
+
+ // bind to the interface's ARP socket
+ xbind(sock_fd, &saddr, sizeof(saddr));
+
+ // get the interface's ethernet address
+ //memset(&ifr, 0, sizeof(ifr));
+ strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
+ xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
+ memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ // start with some stable ip address, either a function of
+ // the hardware address or else the last address we used.
+ // we are taking low-order four bytes, as top-order ones
+ // aren't random enough.
+ // NOTE: the sequence of addresses we try changes only
+ // depending on when we detect conflicts.
+ {
+ uint32_t t;
+ move_from_unaligned32(t, ((char *)&eth_addr + 2));
+ srand(t);
+ }
+ if (ip.s_addr == 0)
+ ip.s_addr = pick();
+
+ // FIXME cases to handle:
+ // - zcip already running!
+ // - link already has local address... just defend/update
+
+ // daemonize now; don't delay system startup
+ if (!FOREGROUND) {
+#if BB_MMU
+ bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
+#endif
+ bb_info_msg("start, interface %s", argv_intf);
+ }
+
+ // run the dynamic address negotiation protocol,
+ // restarting after address conflicts:
+ // - start with some address we want to try
+ // - short random delay
+ // - arp probes to see if another host uses it
+ // - arp announcements that we're claiming it
+ // - use it
+ // - defend it, within limits
+ // exit if:
+ // - address is successfully obtained and -q was given:
+ // run "<script> config", then exit with exitcode 0
+ // - poll error (when does this happen?)
+ // - read error (when does this happen?)
+ // - sendto error (in arp()) (when does this happen?)
+ // - revents & POLLERR (link down). run "<script> deconfig" first
+ state = PROBE;
+ while (1) {
+ struct pollfd fds[1];
+ unsigned deadline_us;
+ struct arp_packet p;
+ int source_ip_conflict;
+ int target_ip_conflict;
+
+ fds[0].fd = sock_fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ // poll, being ready to adjust current timeout
+ if (!timeout_ms) {
+ timeout_ms = random_delay_ms(PROBE_WAIT);
+ // FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
+ // make the kernel filter out all packets except
+ // ones we'd care about.
+ }
+ // set deadline_us to the point in time when we timeout
+ deadline_us = MONOTONIC_US() + timeout_ms * 1000;
+
+ VDBG("...wait %d %s nprobes=%u, nclaims=%u\n",
+ timeout_ms, argv_intf, nprobes, nclaims);
+
+ switch (safe_poll(fds, 1, timeout_ms)) {
+
+ default:
+ //bb_perror_msg("poll"); - done in safe_poll
+ return EXIT_FAILURE;
+
+ // timeout
+ case 0:
+ VDBG("state = %d\n", state);
+ switch (state) {
+ case PROBE:
+ // timeouts in the PROBE state mean no conflicting ARP packets
+ // have been received, so we can progress through the states
+ if (nprobes < PROBE_NUM) {
+ nprobes++;
+ VDBG("probe/%u %s@%s\n",
+ nprobes, argv_intf, inet_ntoa(ip));
+ arp(/* ARPOP_REQUEST, */
+ /* &eth_addr, */ null_ip,
+ &null_addr, ip);
+ timeout_ms = PROBE_MIN * 1000;
+ timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
+ }
+ else {
+ // Switch to announce state.
+ state = ANNOUNCE;
+ nclaims = 0;
+ VDBG("announce/%u %s@%s\n",
+ nclaims, argv_intf, inet_ntoa(ip));
+ arp(/* ARPOP_REQUEST, */
+ /* &eth_addr, */ ip,
+ &eth_addr, ip);
+ timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ }
+ break;
+ case RATE_LIMIT_PROBE:
+ // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
+ // have been received, so we can move immediately to the announce state
+ state = ANNOUNCE;
+ nclaims = 0;
+ VDBG("announce/%u %s@%s\n",
+ nclaims, argv_intf, inet_ntoa(ip));
+ arp(/* ARPOP_REQUEST, */
+ /* &eth_addr, */ ip,
+ &eth_addr, ip);
+ timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ break;
+ case ANNOUNCE:
+ // timeouts in the ANNOUNCE state mean no conflicting ARP packets
+ // have been received, so we can progress through the states
+ if (nclaims < ANNOUNCE_NUM) {
+ nclaims++;
+ VDBG("announce/%u %s@%s\n",
+ nclaims, argv_intf, inet_ntoa(ip));
+ arp(/* ARPOP_REQUEST, */
+ /* &eth_addr, */ ip,
+ &eth_addr, ip);
+ timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ }
+ else {
+ // Switch to monitor state.
+ state = MONITOR;
+ // link is ok to use earlier
+ // FIXME update filters
+ run(argv, "config", &ip);
+ ready = 1;
+ conflicts = 0;
+ timeout_ms = -1; // Never timeout in the monitor state.
+
+ // NOTE: all other exit paths
+ // should deconfig ...
+ if (QUIT)
+ return EXIT_SUCCESS;
+ }
+ break;
+ case DEFEND:
+ // We won! No ARP replies, so just go back to monitor.
+ state = MONITOR;
+ timeout_ms = -1;
+ conflicts = 0;
+ break;
+ default:
+ // Invalid, should never happen. Restart the whole protocol.
+ state = PROBE;
+ ip.s_addr = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ } // switch (state)
+ break; // case 0 (timeout)
+
+ // packets arriving, or link went down
+ case 1:
+ // We need to adjust the timeout in case we didn't receive
+ // a conflicting packet.
+ if (timeout_ms > 0) {
+ unsigned diff = deadline_us - MONOTONIC_US();
+ if ((int)(diff) < 0) {
+ // Current time is greater than the expected timeout time.
+ // Should never happen.
+ VDBG("missed an expected timeout\n");
+ timeout_ms = 0;
+ } else {
+ VDBG("adjusting timeout\n");
+ timeout_ms = (diff / 1000) | 1; /* never 0 */
+ }
+ }
+
+ if ((fds[0].revents & POLLIN) == 0) {
+ if (fds[0].revents & POLLERR) {
+ // FIXME: links routinely go down;
+ // this shouldn't necessarily exit.
+ bb_error_msg("iface %s is down", argv_intf);
+ if (ready) {
+ run(argv, "deconfig", &ip);
+ }
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+
+ // read ARP packet
+ if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
+ bb_perror_msg_and_die(bb_msg_read_error);
+ }
+ if (p.eth.ether_type != htons(ETHERTYPE_ARP))
+ continue;
+#ifdef DEBUG
+ {
+ struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
+ struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
+ struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
+ struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
+ VDBG("%s recv arp type=%d, op=%d,\n",
+ argv_intf, ntohs(p.eth.ether_type),
+ ntohs(p.arp.arp_op));
+ VDBG("\tsource=%s %s\n",
+ ether_ntoa(sha),
+ inet_ntoa(*spa));
+ VDBG("\ttarget=%s %s\n",
+ ether_ntoa(tha),
+ inet_ntoa(*tpa));
+ }
+#endif
+ if (p.arp.arp_op != htons(ARPOP_REQUEST)
+ && p.arp.arp_op != htons(ARPOP_REPLY))
+ continue;
+
+ source_ip_conflict = 0;
+ target_ip_conflict = 0;
+
+ if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0
+ && memcmp(&p.arp.arp_sha, &eth_addr, ETH_ALEN) != 0
+ ) {
+ source_ip_conflict = 1;
+ }
+ if (p.arp.arp_op == htons(ARPOP_REQUEST)
+ && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0
+ && memcmp(&p.arp.arp_tha, &eth_addr, ETH_ALEN) != 0
+ ) {
+ target_ip_conflict = 1;
+ }
+
+ VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
+ state, source_ip_conflict, target_ip_conflict);
+ switch (state) {
+ case PROBE:
+ case ANNOUNCE:
+ // When probing or announcing, check for source IP conflicts
+ // and other hosts doing ARP probes (target IP conflicts).
+ if (source_ip_conflict || target_ip_conflict) {
+ conflicts++;
+ if (conflicts >= MAX_CONFLICTS) {
+ VDBG("%s ratelimit\n", argv_intf);
+ timeout_ms = RATE_LIMIT_INTERVAL * 1000;
+ state = RATE_LIMIT_PROBE;
+ }
+
+ // restart the whole protocol
+ ip.s_addr = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ case MONITOR:
+ // If a conflict, we try to defend with a single ARP probe.
+ if (source_ip_conflict) {
+ VDBG("monitor conflict -- defending\n");
+ state = DEFEND;
+ timeout_ms = DEFEND_INTERVAL * 1000;
+ arp(/* ARPOP_REQUEST, */
+ /* &eth_addr, */ ip,
+ &eth_addr, ip);
+ }
+ break;
+ case DEFEND:
+ // Well, we tried. Start over (on conflict).
+ if (source_ip_conflict) {
+ state = PROBE;
+ VDBG("defend conflict -- starting over\n");
+ ready = 0;
+ run(argv, "deconfig", &ip);
+
+ // restart the whole protocol
+ ip.s_addr = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ default:
+ // Invalid, should never happen. Restart the whole protocol.
+ VDBG("invalid state -- starting over\n");
+ state = PROBE;
+ ip.s_addr = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ } // switch state
+ break; // case 1 (packets arriving)
+ } // switch poll
+ } // while (1)
+#undef argv_intf
+}