diff options
author | nobody | 2004-10-13 09:42:10 +0000 |
---|---|---|
committer | nobody | 2004-10-13 09:42:10 +0000 |
commit | 8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373 (patch) | |
tree | 1826706cd4fd009fcd14f4f8021005ec8ec0fa59 /busybox/networking | |
download | busybox-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.zip busybox-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.gz |
This commit was manufactured by cvs2svn to create tag 'busybox_1_00'.
Diffstat (limited to 'busybox/networking')
98 files changed, 24374 insertions, 0 deletions
diff --git a/busybox/networking/Config.in b/busybox/networking/Config.in new file mode 100644 index 0000000..42176f0 --- /dev/null +++ b/busybox/networking/Config.in @@ -0,0 +1,634 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Networking Utilities" + +config 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. + +config CONFIG_ARPING + bool "arping" + default n + help + Ping hosts by ARP packets + +config CONFIG_FTPGET + bool "ftpget" + default n + help + Retrieve a remote file via FTP. + +config CONFIG_FTPPUT + bool "ftpput" + default n + help + Store a remote file via FTP. + +config CONFIG_HOSTNAME + bool "hostname" + default n + help + Show or set the system's host name + +config CONFIG_HTTPD + bool "httpd" + default n + help + Serve web pages via an HTTP server. + +config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + bool " Support using httpd only from inetd" + default n + depends on CONFIG_HTTPD + help + This option disables uid and port options for the httpd applet + but requires inetd server daemon. + +config CONFIG_FEATURE_HTTPD_BASIC_AUTH + bool " Enable Basic http Authentication" + default y + depends on CONFIG_HTTPD + help + Utilizes password settings from /etc/httpd.conf for basic + authentication on a per url basis. + +config CONFIG_FEATURE_HTTPD_AUTH_MD5 + bool " Support MD5 crypted passwords for http Authentication" + default n + depends on CONFIG_FEATURE_HTTPD_BASIC_AUTH + help + Enables basic per url authentication from /etc/httpd.conf + using md5 passwords. + + +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" + default n + depends on CONFIG_HTTPD + help + This option enables processing of SIGHUP to reload cached + configuration settings. + +config CONFIG_FEATURE_HTTPD_SETUID + bool " Enable support -u <user> option" + default n + depends on CONFIG_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 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" + default n + depends on CONFIG_HTTPD + help + This option enables support for additional MIME types at + run-time to be specified in the configuration file. + +config CONFIG_FEATURE_HTTPD_CGI + bool " Support Common Gateway Interface (CGI)" + default y + depends on CONFIG_HTTPD + help + This option allows scripts and executables to be invoked + when specific urls are requested. + +config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV + bool " Support the REMOTE_PORT environment variable for CGI" + default n + depends on CONFIG_FEATURE_HTTPD_CGI + help + 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." + default y + depends on CONFIG_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 + "<Hello World>". + +config 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)" + default y + depends on CONFIG_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\"" + default n + depends on CONFIG_IFCONFIG + help + 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\"" + default n + depends on CONFIG_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)" + default y + depends on CONFIG_IFCONFIG + help + Set the hardware address of this interface, if the device driver + supports this operation. Currently, we only support the 'ether' + class. + +config CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + bool " Set the broadcast automatically" + default n + depends on CONFIG_IFCONFIG + help + Setting this will make ifconfig attempt to find the broadcast + automatically if the value '+' is used. + +config CONFIG_IFUPDOWN + bool "ifupdown" + default n + help + 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 + 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. + +config CONFIG_FEATURE_IFUPDOWN_IP + bool " Use ip applet" + default n + depends on CONFIG_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 CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN + bool " Use busybox ip applet" + default y + depends on CONFIG_FEATURE_IFUPDOWN_IP + select CONFIG_IP + select CONFIG_FEATURE_IP_ADDRESS + select CONFIG_FEATURE_IP_LINK + select CONFIG_FEATURE_IP_ROUTE + help + Use the busybox iproute "ip" applet to implement "ifupdown". + + If leave this disabled, you must install the full-blown iproute2 + utility or the "ifup" and "ifdown" applets will not work. + +config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN + bool " Use busybox ifconfig and route applets" + default y + depends on CONFIG_IFUPDOWN && !CONFIG_FEATURE_IFUPDOWN_IP + select CONFIG_IFCONFIG + select CONFIG_ROUTE + help + Use the busybox iproute "ifconfig" and "route" applets to + implement the "ifup" and "ifdown" utilities. + + If leave this disabled, you must install the full-blown ifconfig + and route utilities, or the "ifup" and "ifdown" applets will not + work. + +config CONFIG_FEATURE_IFUPDOWN_IPV4 + bool " Enable support for IPv4" + default y + depends on CONFIG_IFUPDOWN + help + If you want busybox to talk IPv4, leave this on. + +config CONFIG_FEATURE_IFUPDOWN_IPV6 + bool " Enable support for IPv6" + default n + depends on CONFIG_IFUPDOWN + help + If you need support for IPv6, turn this option on. + +config CONFIG_FEATURE_IFUPDOWN_IPX + bool " Enable support for IPX" + default n + depends on CONFIG_IFUPDOWN + help + If this option is selected you can use busybox to work with IPX + networks. + +config CONFIG_FEATURE_IFUPDOWN_MAPPING + bool " Enable mapping support" + default n + depends on CONFIG_IFUPDOWN + help + This enables support for the "mapping" stanza, unless you have + a weird network setup you don't need it. + +config CONFIG_INETD + bool "inetd" + default n + help + Internet superserver daemon + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO + bool " Support echo service" + default y + depends on CONFIG_INETD + help + Echo received data internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD + bool " Support discard service" + default y + depends on CONFIG_INETD + help + Internet /dev/null internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME + bool " Support time service" + default y + depends on CONFIG_INETD + help + Return 32 bit time since 1900 internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME + bool " Support daytime service" + default y + depends on CONFIG_INETD + help + Return human-readable time internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN + bool " Support chargen service" + default y + depends on CONFIG_INETD + help + Familiar character generator internal inetd service + + +config 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 + 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 + help + ipcalc takes an IP address and netmask and calculates the + resulting broadcast, network, and host range. + +config CONFIG_FEATURE_IPCALC_FANCY + bool " Fancy IPCALC, more options, adds 1 kbyte" + default y + depends on CONFIG_IPCALC + help + Adds the options hostname, prefix and silent to the output of "ipcalc". + +config CONFIG_IPADDR + bool "ipaddr" + default n + help + Equivalent to selecting address support to "ip", above. + +config CONFIG_IPLINK + bool "iplink" + default n + help + Equivalent to selecting link support to "ip", above. + +config CONFIG_IPROUTE + bool "iproute" + default n + help + Equivalent to selecting route support to "ip", above. + +config CONFIG_IPTUNNEL + bool "iptunnel" + default n + help + Equivalent to selecting tunnel support to "ip", above. + +config CONFIG_NAMEIF + bool "nameif" + default n + help + nameif is used to rename network interface by its MAC address. + Renamed interfaces MUST be in the down state. + 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 separated by space or tab. + File format: + # Comment + new_interface_name XX:XX:XX:XX:XX:XX + +config CONFIG_NC + bool "nc" + default n + help + A simple Unix utility which reads and writes data across network + connections. + +config CONFIG_NETSTAT + bool "netstat" + default n + help + netstat prints information about the Linux networking subsystem. + +config CONFIG_NSLOOKUP + bool "nslookup" + default n + help + nslookup is a tool to query Internet name servers. + +config CONFIG_PING + bool "ping" + default n + help + 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 + bool "ping6" + default n + depends on CONFIG_FEATURE_IPV6 + help + This will give you a ping that can talk IPv6. + +config CONFIG_FEATURE_FANCY_PING6 + bool " Enable fancy ping6 output" + default y + depends on CONFIG_PING6 + help + Make the output from the ping6 applet include statistics, and at the + same time provide full support for ICMP packets. + +config CONFIG_ROUTE + bool "route" + default n + help + Route displays or manipulates the kernel's IP routing tables. + +config 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" + default y + depends on CONFIG_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 + things like ANSI colors and other control sequences behave. + +config CONFIG_FEATURE_TELNET_AUTOLOGIN + bool " Pass USER type to remote host" + default y + depends on CONFIG_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 CONFIG_TELNETD + bool "telnetd" + default n + select CONFIG_LOGIN + help + 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: + CONFIG_UNIX98_PTYS=y + CONFIG_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 CONFIG_LOGIN and + CONFIG_FEATURE_SUID enabled. And finally, you should make + certain that Busybox has been installed setuid root: + + chown root.root /bin/busybox + chmod 4755 /bin/busybox + + with all that done, telnetd _should_ work.... + + +config CONFIG_FEATURE_TELNETD_INETD + bool " Support call from inetd only" + default n + depends on CONFIG_TELNETD + help + Selecting this will make telnetd only callable from inetd, + removing the standalone support. + +config CONFIG_TFTP + bool "tftp" + default n + help + 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" + default y + depends on CONFIG_TFTP + help + Add support for the GET command within the TFTP client. This allows + a client to retrieve a file from a TFTP server. + +config CONFIG_FEATURE_TFTP_PUT + bool " Enable \"put\" command" + default y + depends on CONFIG_TFTP + help + Add support for the PUT command within the TFTP client. This allows + a client to transfer a file to a TFTP server. + +config CONFIG_FEATURE_TFTP_BLOCKSIZE + bool " Enable \"blocksize\" command" + default n + depends on CONFIG_TFTP + help + Allow the client to specify the desired block size for transfers. + +config CONFIG_FEATURE_TFTP_DEBUG + bool " Enable debug" + default n + depends on CONFIG_TFTP + help + 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 + bool "traceroute" + default n + help + Utility to trace the route of IP packets + +config CONFIG_FEATURE_TRACEROUTE_VERBOSE + bool " Enable verbose output" + default n + depends on CONFIG_TRACEROUTE + help + Add some verbosity to traceroute. This includes amongst other things + hostnames and ICMP response types. + +config CONFIG_VCONFIG + bool "vconfig" + default n + help + Creates, removes, and configures VLAN interfaces + +config CONFIG_WGET + bool "wget" + default n + help + 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)" + default y + depends on CONFIG_WGET + help + Enable the transfer progress bar for wget transfers. + +config CONFIG_FEATURE_WGET_AUTHENTICATION + bool " Enable HTTP authentication" + default y + depends on CONFIG_WGET + help + Support authenticated HTTP transfers. + +config CONFIG_FEATURE_WGET_IP6_LITERAL + bool " Enable IPv6 literal addresses" + default y + depends on CONFIG_WGET + help + Support IPv6 address literal notation in URLs. + +source networking/udhcp/Config.in + +endmenu + diff --git a/busybox/networking/Makefile b/busybox/networking/Makefile new file mode 100644 index 0000000..91726b1 --- /dev/null +++ b/busybox/networking/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/networking +NETWORKING_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/networking/Makefile.in b/busybox/networking/Makefile.in new file mode 100644 index 0000000..9bfe901 --- /dev/null +++ b/busybox/networking/Makefile.in @@ -0,0 +1,68 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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:=$(top_builddir)/networking/ +endif +srcdir=$(top_srcdir)/networking +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)) + +$(NETWORKING_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/networking/arping.c b/busybox/networking/arping.c new file mode 100644 index 0000000..7279e86 --- /dev/null +++ b/busybox/networking/arping.c @@ -0,0 +1,499 @@ +/* + * 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. + * + * 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" + +static struct in_addr src; +static struct in_addr dst; +static struct sockaddr_ll me; +static struct sockaddr_ll he; +static struct timeval last; +static int dad; +static int unsolicited; +static int advert; +static int quiet; +static int quit_on_reply = 0; +static int count = -1; +static int timeout; +static int unicasting; +static int s; +static int broadcast_only; +static int sent; +static int brd_sent; +static int received; +static int brd_recv; +static 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) +{ + 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; + + memcpy(p, src_addr, 4); + p += 4; + + if (advert) + memcpy(p, &ME->sll_addr, ah->ar_hln); + else + memcpy(p, &HE->sll_addr, ah->ar_hln); + p += ah->ar_hln; + + memcpy(p, dst_addr, 4); + p += 4; + + gettimeofday(&now, NULL); + err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE)); + if (err == p - buf) { + last = now; + sent++; + if (!unicasting) + brd_sent++; + } + return err; +} + +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 (dad) + exit(!!received); + if (unsolicited) + exit(0); + exit(!received); +} + +void catcher(void) +{ + struct timeval tv; + static struct timeval start; + + gettimeofday(&tv, NULL); + + if (start.tv_sec == 0) + start = tv; + + if (count-- == 0 + || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500)) + finish(); + + if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) { + send_pack(s, &src, &dst, &me, &he); + if (count == 0 && unsolicited) + finish(); + } + alarm(1); +} + +int 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); + + /* Filter out wild packets */ + if (FROM->sll_pkttype != PACKET_HOST && + FROM->sll_pkttype != PACKET_BROADCAST && + FROM->sll_pkttype != PACKET_MULTICAST) + return 0; + + /* Only these types are recognised */ + if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) + return 0; + + /* 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; + + /* 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; + } else { + /* DAD packet was: + src_ip = 0 (or some src) + src_hw = ME + dst_ip = tested address + dst_hw = <unspec> + + We fail, if receive request/reply with: + src_ip = tested_address + src_hw != ME + if src_ip in request was not zero, check + 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 (!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)); + if (dst_ip.s_addr != src.s_addr) { + printf("for %s ", inet_ntoa(dst_ip)); + s_printed = 1; + } + if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) { + if (!s_printed) + printf("for "); + printf("[%s]", + 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); + } else { + printf(" UNSOLICITED?\n"); + } + fflush(stdout); + } + received++; + if (FROM->sll_pkttype != PACKET_HOST) + brd_recv++; + if (ah->ar_op == htons(ARPOP_REQUEST)) + req_recv++; + if (quit_on_reply) + finish(); + if (!broadcast_only) { + memcpy(he.sll_addr, p, me.sll_halen); + unicasting = 1; + } + return 1; +} + +int arping_main(int argc, char **argv) +{ + int socket_errno; + int ch; + uid_t uid = getuid(); + char *device = "eth0"; + int ifindex = 0; + char *source = NULL; + char *target; + + 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; + + if (argc != 1) + bb_show_usage(); + + target = *argv; + + + if (s < 0) { + bb_error_msg("socket"); + exit(socket_errno); + } + + { + 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; + + 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); + } + if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { + bb_error_msg("Interface %s is not ARPable", device); + exit(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 (source && !inet_aton(source, &src)) { + bb_error_msg("invalid source address %s", source); + exit(2); + } + + if (!dad && unsolicited && src.s_addr == 0) + src = dst; + + if (!dad || src.s_addr) { + struct sockaddr_in saddr; + int probe_fd = socket(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); + } + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + if (src.s_addr) { + 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); + + 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); + } + src = saddr.sin_addr; + } + close(probe_fd); + }; + + me.sll_family = AF_PACKET; + me.sll_ifindex = ifindex; + me.sll_protocol = htons(ETH_P_ARP); + if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) { + bb_error_msg("bind"); + exit(2); + } + + { + int alen = sizeof(me); + + if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) { + bb_error_msg("getsockname"); + exit(2); + } + } + if (me.sll_halen == 0) { + bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device); + exit(dad ? 0 : 2); + } + he = me; + memset(he.sll_addr, -1, he.sll_halen); + + if (!quiet) { + printf("ARPING to %s", inet_ntoa(dst)); + printf(" from %s via %s\n", inet_ntoa(src), + device ? device : "unknown"); + } + + 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); + } + + catcher(); + + while (1) { + sigset_t sset, osset; + char packet[4096]; + struct sockaddr_ll from; + int alen = sizeof(from); + int cc; + + if ((cc = recvfrom(s, packet, sizeof(packet), 0, + (struct sockaddr *) &from, &alen)) < 0) { + perror("recvfrom"); + continue; + } + sigemptyset(&sset); + sigaddset(&sset, SIGALRM); + sigaddset(&sset, SIGINT); + sigprocmask(SIG_BLOCK, &sset, &osset); + recv_pack(packet, cc, &from); + sigprocmask(SIG_SETMASK, &osset, NULL); + } +} diff --git a/busybox/networking/ftpgetput.c b/busybox/networking/ftpgetput.c new file mode 100644 index 0000000..f6bd82b --- /dev/null +++ b/busybox/networking/ftpgetput.c @@ -0,0 +1,378 @@ +/* vi: set sw=4 ts=4: */ +/* + * 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@iinet.net.au> + * + * 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 + * + */ + +#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 <sys/socket.h> +#include <arpa/inet.h> + +#include "busybox.h" + +typedef struct ftp_host_info_s { + char *user; + char *password; + struct sockaddr_in *s_in; +} ftp_host_info_t; + +static char verbose_flag = 0; +static char do_continue = 0; + +static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) +{ + if (verbose_flag) { + bb_error_msg("cmd %s%s", s1, s2); + } + + if (s1) { + if (s2) { + fprintf(stream, "%s%s\n", s1, s2); + } else { + fprintf(stream, "%s\n", s1); + } + } + do { + char *buf_ptr; + + if (fgets(buf, 510, stream) == NULL) { + bb_perror_msg_and_die("fgets()"); + } + buf_ptr = strstr(buf, "\r\n"); + if (buf_ptr) { + *buf_ptr = '\0'; + } + } while (! isdigit(buf[0]) || buf[3] != ' '); + + return atoi(buf); +} + +static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf) +{ + char *buf_ptr; + unsigned short port_num; + + buf_ptr = strrchr(buf, ','); + *buf_ptr = '\0'; + port_num = atoi(buf_ptr + 1); + + buf_ptr = strrchr(buf, ','); + *buf_ptr = '\0'; + port_num += atoi(buf_ptr + 1) * 256; + + server->s_in->sin_port=htons(port_num); + return(xconnect(server->s_in)); +} + +static FILE *ftp_login(ftp_host_info_t *server) +{ + FILE *control_stream; + char buf[512]; + + /* Connect to the command socket */ + control_stream = fdopen(xconnect(server->s_in), "r+"); + if (control_stream == NULL) { + bb_perror_msg_and_die("Couldnt open control stream"); + } + + if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { + bb_error_msg_and_die("%s", buf + 4); + } + + /* 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); + } + + ftpcmd("TYPE I", NULL, control_stream, buf); + + return(control_stream); +} + +#ifdef CONFIG_FTPGET +static int ftp_recieve(ftp_host_info_t *server, FILE *control_stream, + const char *local_path, char *server_path) +{ + char buf[512]; + off_t filesize = 0; + int fd_data; + int fd_local = -1; + off_t beg_range = 0; + + /* 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(server, buf); + + if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) { + unsigned long value=filesize; + if (safe_strtoul(buf + 4, &value)) + bb_error_msg_and_die("SIZE error: %s", buf + 4); + filesize = value; + } + + if ((local_path[0] == '-') && (local_path[1] == '\0')) { + fd_local = STDOUT_FILENO; + do_continue = 0; + } + + if (do_continue) { + struct stat sbuf; + if (lstat(local_path, &sbuf) < 0) { + bb_perror_msg_and_die("fstat()"); + } + if (sbuf.st_size > 0) { + beg_range = sbuf.st_size; + } else { + do_continue = 0; + } + } + + if (do_continue) { + sprintf(buf, "REST %ld", (long)beg_range); + if (ftpcmd(buf, NULL, control_stream, buf) != 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 (fd_local == -1) { + if (do_continue) { + fd_local = bb_xopen(local_path, O_APPEND | O_WRONLY); + } else { + fd_local = bb_xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY); + } + } + + /* Copy the file */ + if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) { + exit(EXIT_FAILURE); + } + + /* 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); +} +#endif + +#ifdef CONFIG_FTPPUT +static int ftp_send(ftp_host_info_t *server, FILE *control_stream, + 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(server, buf); + + if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) { + bb_error_msg_and_die("CWD error: %s", buf + 4); + } + + /* get the local file */ + if ((local_path[0] == '-') && (local_path[1] == '\0')) { + fd_local = STDIN_FILENO; + } else { + 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; + } + } + response = ftpcmd("STOR ", local_path, control_stream, buf); + 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_eof(fd_local, fd_data) == -1) { + exit(EXIT_FAILURE); + } + + /* 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); +} +#endif + +#define FTPGETPUT_OPT_CONTINUE 1 +#define FTPGETPUT_OPT_VERBOSE 2 +#define FTPGETPUT_OPT_USER 4 +#define FTPGETPUT_OPT_PASSWORD 8 +#define FTPGETPUT_OPT_PORT 16 + +static const struct option ftpgetput_long_options[] = { + {"continue", 1, NULL, 'c'}, + {"verbose", 0, NULL, 'v'}, + {"username", 1, NULL, 'u'}, + {"password", 1, NULL, 'p'}, + {"port", 1, NULL, 'P'}, + {0, 0, 0, 0} +}; + +int ftpgetput_main(int argc, char **argv) +{ + /* content-length of the file */ + unsigned long opt; + char *port = "ftp"; + + /* socket to ftp server */ + FILE *control_stream; + struct sockaddr_in s_in; + + /* continue a prev transfer (-c) */ + ftp_host_info_t *server; + + int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL; + + /* Check to see if the command is ftpget or ftput */ +#ifdef CONFIG_FTPPUT +# ifdef CONFIG_FTPGET + if (bb_applet_name[3] == 'p') { + ftp_action = ftp_send; + } +# else + ftp_action = ftp_send; +# endif +#endif +#ifdef CONFIG_FTPGET +# ifdef CONFIG_FTPPUT + if (bb_applet_name[3] == 'g') { + ftp_action = ftp_recieve; + } +# else + ftp_action = ftp_recieve; +# endif +#endif + + /* Set default values */ + server = xmalloc(sizeof(ftp_host_info_t)); + server->user = "anonymous"; + server->password = "busybox@"; + verbose_flag = 0; + + /* + * Decipher the command line + */ + bb_applet_long_options = ftpgetput_long_options; + opt = bb_getopt_ulflags(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); + + /* Process the non-option command line arguments */ + if (argc - optind != 3) { + bb_show_usage(); + } + + if (opt & FTPGETPUT_OPT_CONTINUE) { + do_continue = 1; + } + if (opt & FTPGETPUT_OPT_VERBOSE) { + verbose_flag = 1; + } + + /* 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... */ + server->s_in = &s_in; + bb_lookup_host(&s_in, argv[optind]); + s_in.sin_port = bb_lookup_port(port, "tcp", 21); + if (verbose_flag) { + printf("Connecting to %s[%s]:%d\n", + argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port)); + } + + /* Connect/Setup/Configure the FTP session */ + control_stream = ftp_login(server); + + return(ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2])); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/networking/hostname.c b/busybox/networking/hostname.c new file mode 100644 index 0000000..a00263d --- /dev/null +++ b/busybox/networking/hostname.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: hostname.c,v 1.36 2003/07/14 21:21:01 andersen Exp $ + * Mini hostname implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> + * + * 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 + */ + +#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 */ + +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"); + } + } 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 + } +} + +int hostname_main(int argc, char **argv) +{ + int opt; + int type = 0; + struct hostent *hp; + char *filename = NULL; + char buf[255]; + char *p = NULL; + + 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(); + } + } + + /* Output in desired format */ + if (type != 0) { + gethostname(buf, 255); + hp = xgethostbyname(buf); + p = strchr(hp->h_name, '.'); + if (type == 'f') { + puts(hp->h_name); + } else if (type == 's') { + if (p != NULL) { + *p = 0; + } + puts(hp->h_name); + } else if (type == 'd') { + if (p) puts(p + 1); + } else if (type == 'i') { + while (hp->h_addr_list[0]) { + printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++))); + } + printf("\n"); + } + } + /* Set the hostname */ + else if (filename != NULL) { + do_sethostname(filename, 1); + } else if (optind < argc) { + do_sethostname(argv[optind], 0); + } + /* Or if all else fails, + * just print the current hostname */ + else { + gethostname(buf, 255); + puts(buf); + } + return(0); +} diff --git a/busybox/networking/httpd.c b/busybox/networking/httpd.c new file mode 100644 index 0000000..83ded53 --- /dev/null +++ b/busybox/networking/httpd.c @@ -0,0 +1,2091 @@ +/* + * httpd implementation for busybox + * + * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org> + * Copyright (C) 2003,2004 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 + * + ***************************************************************************** + * + * Typical usage: + * for non root user + * httpd -p 8080 -h $HOME/public_html + * or for daemon start from rc script with uid=0: + * httpd -u www + * This is equivalent if www user have uid=80 to + * 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 + * server changes directory to the location of the script and executes it + * after setting QUERY_STRING and other environment variables. + * + * The server 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 "<Hello World>" + * Note that url encoding for arguments is not the same as html encoding for + * presentation. -d decodes a url-encoded argument while -e encodes in html + * for page display. + * + * httpd.conf has the following format: + * + * 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 + * /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 insensitive + * 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 + * - 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 + * + * Example: + * 1. Allow only specified addresses + * A:172.20 # Allow any address that begins with 172.20. + * A:10.10. # Allow any address that begins with 10.10. + * 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. + * + * subdir paths are relative to the containing subdir and thus cannot + * affect the parent rules. + * + * Note that since the sub dir is parsed in the forked thread servicing the + * 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 + * root configuration file. If -c is set and the file is not found, the + * server exits with an error. + * +*/ + + +#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.35 6-Oct-2004"; +static const char default_path_httpd_conf[] = "/etc"; +static const char httpd_conf[] = "httpd.conf"; +static const char home[] = "./"; + +#ifdef CONFIG_LFS +# define cont_l_fmt "%lld" +#else +# define cont_l_fmt "%ld" +#endif + +#define TIMEOUT 60 + +// Note: busybox 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_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_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_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); +} +#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 MAX_MEMORY_BUFF 8192 /* IO buffer */ + +typedef struct HT_ACCESS { + char *after_colon; + struct HT_ACCESS *next; + char before_colon[1]; /* really bigger, must last */ +} Htaccess; + +typedef struct HT_ACCESS_IP { + unsigned int ip; + unsigned int mask; + int allow_deny; + struct HT_ACCESS_IP *next; +} Htaccess_IP; + +typedef struct +{ + char buf[MAX_MEMORY_BUFF]; + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + const char *realm; + char *remoteuser; +#endif + + const char *query; + +#ifdef CONFIG_FEATURE_HTTPD_CGI + char *referer; +#endif + + const char *configFile; + + unsigned int rmt_ip; +#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG) + char rmt_ip_str[16]; /* for set env REMOTE_ADDR */ +#endif + unsigned port; /* server initial port and for + set env REMOTE_PORT */ + union HTTPD_FOUND { + const char *found_mime_type; + const char *found_moved_temporarily; + } httpd_found; + + off_t ContentLength; /* -1 - unknown */ + time_t last_mod; + + Htaccess_IP *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 + +#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 + volatile int alarm_signaled; + +} HttpdConfig; + +static HttpdConfig *config; + +static const char request_GET[] = "GET"; /* size algorithic optimize */ + +static const char* const suffixTable [] = { +/* Warning: shorted 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 + 0, "application/octet-stream" /* default */ + }; + +typedef enum +{ + HTTP_OK = 200, + 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, +#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_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_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." }, + { HTTP_REQUEST_TIMEOUT, "Request Timeout", + "No request appeared within a reasonable time period." }, + { 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_NOT_MODIFIED, "Not Modified" }, + { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, + { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, +#endif +}; + + +static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; +static const char Content_length[] = "Content-length:"; + + +static int +scan_ip (const char **ep, unsigned int *ip, unsigned char endc) +{ + const char *p = *ep; + int auto_mask = 8; + int j; + + *ip = 0; + for (j = 0; j < 4; j++) { + unsigned int octet; + + if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0) + 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 != 0) + auto_mask += 8; + *ip = ((*ip) << 8) | octet; + } + if (*p != 0) { + if (*p != endc) + return -auto_mask; + p++; + if(*p == 0) + return -auto_mask; + } + *ep = p; + return auto_mask; +} + +static int +scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask) +{ + int i; + unsigned int msk; + + i = scan_ip(&ipm, ip, '/'); + if(i < 0) + return i; + if(*ipm) { + const char *p = ipm; + + i = 0; + while (*p) { + if (*p < '0' || *p > '9') { + if (*p == '.') { + i = scan_ip (&ipm, mask, 0); + return i != 32; + } + return -1; + } + i *= 10; + i += *p - '0'; + p++; + } + } + if (i > 32 || i < 0) + return -1; + msk = 0x80000000; + *mask = 0; + while (i > 0) { + *mask |= msk; + msk >>= 1; + i--; + } + return 0; +} + +#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) +static void free_config_lines(Htaccess **pprev) +{ + Htaccess *prev = *pprev; + + while( prev ) { + Htaccess *cur = prev; + + prev = cur->next; + free(cur); + } + *pprev = NULL; +} +#endif + +/* 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 + * + * 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) + * + ****************************************************************************/ +static void parse_conf(const char *path, int flag) +{ + FILE *f; +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + Htaccess *prev, *cur; +#elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + Htaccess *cur; +#endif + + const char *cf = config->configFile; + char buf[160]; + char *p0 = NULL; + char *c, *p; + + /* free previous ip setup if present */ + Htaccess_IP *pip = config->ip_a_d; + + while( pip ) { + Htaccess_IP *cur_ipl = pip; + + pip = cur_ipl->next; + free(cur_ipl); + } + config->ip_a_d = NULL; + + config->flg_deny_all = 0; + +#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) + /* 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 +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + free_config_lines(&config->mime_a); +#endif + } +#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; + } + 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; + } + 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; + } + } + *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(*p0 == 'a') + *p0 = 'A'; + else if(*p0 != 'D' && *p0 != 'A' +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + && *p0 != '/' +#endif +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + && *p0 != '.' +#endif + ) + continue; + if(*p0 == 'A' || *p0 == 'D') { + /* storing current config IP line */ + pip = calloc(1, sizeof(Htaccess_IP)); + if(pip) { + if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) { + /* syntax IP{/mask} error detected, protect all */ + *p0 = 'D'; + pip->mask = 0; + } + pip->allow_deny = *p0; + if(*p0 == 'D') { + /* Deny:form_IP move top */ + pip->next = config->ip_a_d; + config->ip_a_d = pip; + } else { + /* add to bottom A:form_IP config line */ + Htaccess_IP *prev_IP = config->ip_a_d; + + if(prev_IP == NULL) { + config->ip_a_d = pip; + } else { + while(prev_IP->next) + prev_IP = prev_IP->next; + prev_IP->next = pip; + } + } + } + continue; + } +#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 */ + } + 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 + +#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) + /* 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_CONFIG_WITH_MIME_TYPES + if(*cf == '.') { + /* config .mime line move top for overwrite previous */ + cur->next = config->mime_a; + config->mime_a = cur; + continue; + } +#endif +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + free(p0); + 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; + } + break; + } + if(prev_hti != hti) + prev_hti = prev_hti->next; + } + if(!hti) { /* not inserted, add to bottom */ + prev->next = cur; + prev = cur; + } + } +#endif + } +#endif + } + 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. + * + ****************************************************************************/ +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; +} +#endif /* CONFIG_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) +{ + /* 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; +} + + +#ifdef CONFIG_FEATURE_HTTPD_CGI +/**************************************************************************** + * + > $Function: addEnv() + * + * $Description: Add an environment 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 = NULL; + const char *underline; + + if (!value) + value = ""; + underline = *name_after_underline ? "_" : ""; + asprintf(&s, "%s%s%s=%s", name_before_underline, underline, + name_after_underline, value); + if(s) { + putenv(s); + } +} + +#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) +{ + char buf[16]; + + sprintf(buf, "%u", config->port); + addEnv(port_name, "PORT", buf); +} +#endif +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + + +#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) +{ + + 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; +} +#endif + + +#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) +{ + 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; +} +#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) +{ + char *buf = config->buf; + const char *responseString = ""; + const char *infoString = 0; + const char *mime_type; + 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; + break; + } + } + /* error message is HTML */ + mime_type = responseNum == HTTP_OK ? + config->httpd_found.found_mime_type : "text/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, 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(responseNum == HTTP_MOVED_TEMPORARILY) { + len += sprintf(buf+len, "Location: %s/%s%s\r\n", + config->httpd_found.found_moved_temporarily, + (config->query ? "?" : ""), + (config->query ? config->query : "")); + } + + if (config->ContentLength != -1) { /* file */ + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod)); + len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\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); +} + +/**************************************************************************** + * + > $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. + * + * $Return: (int) . . . . number of characters read. -1 if error. + * + ****************************************************************************/ +static int getLine(void) +{ + int count = 0; + char *buf = config->buf; + + 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; +} + +#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 /). + * (int bodyLen) . . . . . . . . Length of the post body. + * (const char *cookie) . . . . . For set HTTP_COOKIE. + * (const char *content_type) . . For set CONTENT_TYPE. + + * + * $Return: (char *) . . . . A pointer to the decoded string (same as input). + * + * $Errors: None + * + ****************************************************************************/ +static int sendCgi(const char *url, + const char *request, int bodyLen, const char *cookie, + const char *content_type) +{ + 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; + + *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(config->query) { + char *uri = alloca(strlen(purl) + 2 + strlen(config->query)); + if(uri) + sprintf(uri, "%s?%s", purl, config->query); + 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", "", config->query); + addEnv("SERVER", "SOFTWARE", httpdVersion); + addEnv("SERVER", "PROTOCOL", "HTTP/1.0"); + addEnv("GATEWAY_INTERFACE", "", "CGI/1.1"); + addEnv("REMOTE", "ADDR", config->rmt_ip_str); +#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV + addEnvPort("REMOTE"); +#endif + if(bodyLen) { + char sbl[32]; + + sprintf(sbl, "%d", bodyLen); + addEnv("CONTENT", "LENGTH", sbl); + } + if(cookie) + addEnv("HTTP", "COOKIE", cookie); + if(content_type) + addEnv("CONTENT", "TYPE", content_type); +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + if(config->remoteuser) { + addEnv("REMOTE", "USER", config->remoteuser); + addEnv("AUTH_TYPE", "", "Basic"); + } +#endif + if(config->referer) + addEnv("HTTP", "REFERER", config->referer); + + /* 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. + execv(realpath_buff, argp); + } + } + } +#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; + size_t post_readed_size = 0, post_readed_idx = 0; + + inFd = fromCgi[0]; + outFd = toCgi[1]; + close(fromCgi[1]); + close(toCgi[0]); + signal(SIGPIPE, SIG_IGN); + + while (1) { + fd_set readSet; + fd_set writeSet; + char wbuf[128]; + int nfound; + int count; + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_SET(inFd, &readSet); + if(bodyLen > 0 || post_readed_size > 0) { + FD_SET(outFd, &writeSet); + nfound = outFd > inFd ? outFd : inFd; + if(post_readed_size == 0) { + FD_SET(a_c_r, &readSet); + if(nfound < a_c_r) + nfound = a_c_r; + } + /* Now wait on the set of sockets! */ + nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); + } else { + if(!bodyLen) { + close(outFd); + bodyLen = -1; + } + nfound = select(inFd + 1, &readSet, 0, 0, NULL); + } + + 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 + break; + } + } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { + count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size); + if(count > 0) { + post_readed_size -= count; + post_readed_idx += count; + if(post_readed_size == 0) + post_readed_idx = 0; + } + } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) { + count = bodyLen > sizeof(wbuf) ? sizeof(wbuf) : bodyLen; + count = safe_read(a_c_r, wbuf, count); + if(count > 0) { + post_readed_size += count; + bodyLen -= count; + } else { + bodyLen = 0; /* closed */ + } + } + if(FD_ISSET(inFd, &readSet)) { + int s = a_c_w; + char *rbuf = config->buf; + +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif +#if PIPESIZE >= MAX_MEMORY_BUFF +# error "PIPESIZE >= MAX_MEMORY_BUFF" +#endif + + // There is something to read + count = safe_read(inFd, rbuf, PIPESIZE); + if (count == 0) + break; /* closed */ + if (count > 0) { + if (firstLine) { + rbuf[count] = 0; + /* check to see if the user script added headers */ + if(strncmp(rbuf, "HTTP/1.0 200 OK\n", 4) != 0) { + bb_full_write(s, "HTTP/1.0 200 OK\n", 16); + } + if (strstr(rbuf, "ontent-") == 0) { + bb_full_write(s, "Content-type: text/plain\n\n", 26); + } + firstLine = 0; + } + if (bb_full_write(s, rbuf, count) != count) + break; + +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr, "cgi read %d bytes\n", count); +#endif + } + } + } + } + return 0; +} +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + +/**************************************************************************** + * + > $Function: sendFile() + * + * $Description: Send a file response to an HTTP request + * + * $Parameter: + * (const char *) url . . The URL requested. + * + * $Return: (int) . . . . . . Always 0. + * + ****************************************************************************/ +static int sendFile(const char *url) +{ + char * suffix; + int f; + const char * const * table; + const char * try_suffix; + + suffix = strrchr(url, '.'); + + 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 == '.') + break; + } + /* also, if not found, set default as "application/octet-stream"; */ + config->httpd_found.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->httpd_found.found_mime_type = cur->after_colon; + break; + } + } + } +#endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */ + +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr, "Sending file '%s' Content-type: %s\n", + url, config->httpd_found.found_mime_type); +#endif + + f = open(url, O_RDONLY); + if (f >= 0) { + int count; + char *buf = config->buf; + + sendHeaders(HTTP_OK); + while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) { + if (bb_full_write(a_c_w, buf, count) != count) + break; + } + close(f); + } else { +#ifdef DEBUG + if (config->debugHttpd) + bb_perror_msg("Unable to open '%s'", url); +#endif + sendHeaders(HTTP_NOT_FOUND); + } + + return 0; +} + +static int checkPermIP(void) +{ + Htaccess_IP * cur; + + /* This could stand some work */ + for (cur = config->ip_a_d; cur; cur = cur->next) { +#ifdef DEBUG + if (config->debugHttpd) { + fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str); + fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n", + (unsigned char)(cur->ip >> 24), + (unsigned char)(cur->ip >> 16), + (unsigned char)(cur->ip >> 8), + cur->ip & 0xff, + (unsigned char)(cur->mask >> 24), + (unsigned char)(cur->mask >> 16), + (unsigned char)(cur->mask >> 8), + cur->mask & 0xff); + } +#endif + if((config->rmt_ip & cur->mask) == cur->ip) + return cur->allow_deny == 'A'; /* Allow/Deny */ + } + + /* if unconfigured, return 1 - access from all */ + return !config->flg_deny_all; +} + +/**************************************************************************** + * + > $Function: checkPerm() + * + * $Description: Check the permission file for access password protected. + * + * 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. + * (const char *) request . . . User information to validate. + * + * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. + * + ****************************************************************************/ + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH +static int checkPerm(const char *path, const char *request) +{ + Htaccess * cur; + const char *p; + const char *p0; + + const char *prev = NULL; + + /* This could stand some work */ + for (cur = 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", p0, request); +#endif + { + int l = strlen(p0); + + if(strncmp(p0, path, l) == 0 && + (l == 1 || path[l] == '/' || path[l] == 0)) { + char *u; + /* path match found. Check request */ + /* for check next /path:user:password */ + prev = p0; + u = strchr(request, ':'); + if(u == NULL) { + /* bad request, ':' required */ + break; + } + +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + { + char *cipher; + char *pp; + + 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) + goto set_remoteuser_var; /* Ok */ + /* unauthorized */ + continue; + } + } +#endif + if (strcmp(p, request) == 0) { +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 +set_remoteuser_var: +#endif + config->remoteuser = strdup(request); + if(config->remoteuser) + config->remoteuser[(u - request)] = 0; + return 1; /* Ok */ + } + /* unauthorized */ + } + } + } /* for */ + + return prev == NULL; +} + +#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ + +/**************************************************************************** + * + > $Function: handleIncoming() + * + * $Description: Handle an incoming http request. + * + ****************************************************************************/ + +static void +handle_sigalrm( int sig ) +{ + sendHeaders(HTTP_REQUEST_TIMEOUT); + config->alarm_signaled = sig; +} + +/**************************************************************************** + * + > $Function: handleIncoming() + * + * $Description: Handle an incoming http request. + * + ****************************************************************************/ +static void handleIncoming(void) +{ + char *buf = config->buf; + char *url; + char *purl; + int blank = -1; + char *test; + struct stat sb; + int ip_allowed; +#ifdef CONFIG_FEATURE_HTTPD_CGI + const char *prequest = request_GET; + long length=0; + char *cookie = 0; + char *content_type = 0; +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + fd_set s_fd; + struct timeval tv; + int retval; +#endif + struct sigaction sa; + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + int credentials = -1; /* if not requred this is Ok */ +#endif + + sa.sa_handler = handle_sigalrm; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* no SA_RESTART */ + sigaction(SIGALRM, &sa, NULL); + + do { + int count; + + (void) alarm( TIMEOUT ); + if (getLine() <= 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; + } + } +#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 */ + test = strchr(url, '?'); + if (test) { + *test++ = 0; + config->query = test; + } + + /* 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; + } + } + } + *++purl = *test; + } while (*++test); + + *++purl = 0; /* so keep last character */ + test = purl; /* end ptr */ + + /* If URL is directory, adding '/' */ + /* If URL is directory, adding '/' */ + if(test[-1] != '/') { + if ( is_directory(url + 1, 1, &sb) ) { + config->httpd_found.found_moved_temporarily = url; + } + } +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr, "url='%s', args=%s\n", url, config->query); +#endif + + test = url; + ip_allowed = checkPermIP(); + 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 = checkPermIP(); + } + *test = '/'; + } + + // read until blank line for HTTP version specified, else parse immediate + while (blank >= 0 && alarm(TIMEOUT) >= 0 && (count = getLine()) > 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); + } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) { + for(test = buf + 13; isspace(*test); test++) + ; + content_type = strdup(test); + } else if ((strncasecmp(buf, "Referer:", 8) == 0)) { + for(test = buf + 8; isspace(*test); test++) + ; + config->referer = 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 */ + + (void) alarm( 0 ); + if(config->alarm_signaled) + break; + + 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 + + if(config->httpd_found.found_moved_temporarily) { + sendHeaders(HTTP_MOVED_TEMPORARILY); +#ifdef DEBUG + /* clear unforked memory flag */ + if(config->debugHttpd) + config->httpd_found.found_moved_temporarily = NULL; +#endif + break; + } + + test = url + 1; /* skip first '/' */ + +#ifdef CONFIG_FEATURE_HTTPD_CGI + /* if strange Content-Length */ + if (length < 0) + break; + + if (strncmp(test, "cgi-bin", 7) == 0) { + if(test[7] == '/' && test[8] == 0) + goto FORBIDDEN; // protect listing cgi-bin/ + sendCgi(url, prequest, length, cookie, content_type); + } 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; + } + sendFile(test); +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + /* unset if non inetd looped */ + config->ContentLength = -1; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + } + } +#endif + + } while (0); + + +#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(cookie); + free(content_type); + free(config->referer); +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + free(config->remoteuser); +#endif +# endif + shutdown(a_c_w, SHUT_WR); + + /* Properly wait for remote to closed */ + FD_ZERO (&s_fd) ; + FD_SET (a_c_w, &s_fd) ; + + do { + tv.tv_sec = 2 ; + tv.tv_usec = 0 ; + retval = select (a_c_w + 1, &s_fd, NULL, NULL, &tv); + } while (retval > 0 && (read (a_c_w, buf, sizeof (config->buf)) > 0)); + + shutdown(a_c_r, SHUT_RD); + close(config->accepted_socket); +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ +} + +/**************************************************************************** + * + > $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. + * + * $Return: (int) . . . . Always 0. + * + ****************************************************************************/ +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +static int miniHttpd(int server) +{ + fd_set readfd, portfd; + + FD_ZERO(&portfd); + FD_SET(server, &portfd); + + /* copy the ports we are watching to the readfd set */ + while (1) { + readfd = portfd; + + /* Now wait INDEFINITELY 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; + + socklen_t fromAddrLen = sizeof(fromAddr); + int s = accept(server, + (struct sockaddr *)&fromAddr, &fromAddrLen); + + if (s < 0) { + continue; + } + config->accepted_socket = s; + config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr); +#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG) + sprintf(config->rmt_ip_str, "%u.%u.%u.%u", + (unsigned char)(config->rmt_ip >> 24), + (unsigned char)(config->rmt_ip >> 16), + (unsigned char)(config->rmt_ip >> 8), + config->rmt_ip & 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_str, config->port); + } +#endif +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + + /* set the KEEPALIVE option to cull dead connections */ + on = 1; + setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on)); + + 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; +} + +#else + /* from inetd */ + +static int miniHttpd(void) +{ + struct sockaddr_in fromAddrLen; + socklen_t sinlen = sizeof (struct sockaddr_in); + + getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen); + config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr); +#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG) + sprintf(config->rmt_ip_str, "%u.%u.%u.%u", + (unsigned char)(config->rmt_ip >> 24), + (unsigned char)(config->rmt_ip >> 16), + (unsigned char)(config->rmt_ip >> 8), + config->rmt_ip & 0xff); +#endif + config->port = ntohs(fromAddrLen.sin_port); + handleIncoming(); + return 0; +} +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ + +#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP +static void sighup_handler(int sig) +{ + /* set and reset */ + struct sigaction sa; + + parse_conf(default_path_httpd_conf, + sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); + sa.sa_handler = sighup_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGHUP, &sa, NULL); +} +#endif + + +static const char httpd_opts[]="c:d:h:" +#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR + "e:" +#define OPT_INC_1 1 +#else +#define OPT_INC_1 0 +#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 +#endif +#else +#define OPT_INC_2 0 +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + "p:v" +#ifdef CONFIG_FEATURE_HTTPD_SETUID + "u:" +#endif +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ + ; + +#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)) + + +#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; +#endif + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + int server; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_SETUID + const char *s_uid; + long uid = -1; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + const char *pass; +#endif + + config = xcalloc(1, sizeof(*config)); +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + config->realm = "Web Server Authentication"; +#endif + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + config->port = 80; +#endif + + config->ContentLength = -1; + + 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 +#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; + } +#endif +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + if(opt & OPT_MD5) { + printf("%s\n", pw_encrypt(pass, "$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); + } + } +#endif +#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 privileges */ + if(uid > 0) + setuid(uid); +# endif +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + { + char *p = getenv("PATH"); + if(p) { + p = bb_xstrdup(p); + } + clearenv(); + if(p) + setenv("PATH", p, 1); +# ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + addEnvPort("SERVER"); +# endif + } +#endif + +#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP + sighup_handler(0); +#else + parse_conf(default_path_httpd_conf, FIRST_PARSE); +#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); +#else + return miniHttpd(); +#endif +} diff --git a/busybox/networking/ifconfig.c b/busybox/networking/ifconfig.c new file mode 100644 index 0000000..4e3df29 --- /dev/null +++ b/busybox/networking/ifconfig.c @@ -0,0 +1,605 @@ +/* ifconfig + * + * Similar to the standard Unix ifconfig, but with only the necessary + * parts for AF_INET, and without any printing of if info (for now). + * + * Bjorn Wesen, Axis Communications AB + * + * + * 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.30 2004/03/31 11:30:08 andersen Exp $ + * + */ + +/* + * Heavily modified by Manuel Novoa III Mar 6, 2001 + * + * From initial port to busybox, removed most of the redundancy by + * converting to a table-driven approach. Added several (optional) + * args missing from initial port. + * + * Still missing: media, tunnel. + * + * 2002-04-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 <netdb.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_ether.h> +#endif +#include "inet_common.h" +#include "busybox.h" + +#ifdef CONFIG_FEATURE_IFCONFIG_SLIP +# include <net/if_slip.h> +#endif + +/* I don't know if this is needed for busybox or not. Anyone? */ +#define QUESTIONABLE_ALIAS_CASE + + +/* 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 IFF_DYNAMIC +# define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ +#endif + +#ifdef CONFIG_FEATURE_IPV6 +struct in6_ifreq { + struct in6_addr ifr6_addr; + uint32_t ifr6_prefixlen; + int ifr6_ifindex; +}; +#endif + +/* + * Here are the bit masks for the "flags" member of struct options below. + * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'. + * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg. + */ +#define N_CLR 0x01 +#define M_CLR 0x02 +#define N_SET 0x04 +#define M_SET 0x08 +#define N_ARG 0x10 +#define M_ARG 0x20 + +#define M_MASK (M_CLR | M_SET | M_ARG) +#define N_MASK (N_CLR | N_SET | N_ARG) +#define SET_MASK (N_SET | M_SET) +#define CLR_MASK (N_CLR | M_CLR) +#define SET_CLR_MASK (SET_MASK | CLR_MASK) +#define ARG_MASK (M_ARG | N_ARG) + +/* + * Here are the bit masks for the "arg_flags" member of struct options below. + */ + +/* + * cast type: + * 00 int + * 01 char * + * 02 HOST_COPY in_ether + * 03 HOST_COPY INET_resolve + */ +#define A_CAST_TYPE 0x03 +/* + * map type: + * 00 not a map type (mem_start, io_addr, irq) + * 04 memstart (unsigned long) + * 08 io_addr (unsigned short) + * 0C irq (unsigned char) + */ +#define A_MAP_TYPE 0x0C +#define A_ARG_REQ 0x10 /* Set if an arg is required. */ +#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 +#define A_HOSTNAME 0x100 /* Set if it is ip addr. */ +#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ +#else +#define A_HOSTNAME 0 +#define A_BROADCAST 0 +#endif + +/* + * These defines are for dealing with the A_CAST_TYPE field. + */ +#define A_CAST_CHAR_PTR 0x01 +#define A_CAST_RESOLVE 0x01 +#define A_CAST_HOST_COPY 0x02 +#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY +#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE) + +/* + * These defines are for dealing with the A_MAP_TYPE field. + */ +#define A_MAP_ULONG 0x04 /* memstart */ +#define A_MAP_USHORT 0x08 /* io_addr */ +#define A_MAP_UCHAR 0x0C /* irq */ + +/* + * Define the bit masks signifying which operations to perform for each arg. + */ + +#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG) +#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG) +#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR) +#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE) +#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_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) +#define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) + + +/* + * Set up the tables. Warning! They must have corresponding order! + */ + +struct arg1opt { + const char *name; + unsigned short selector; + unsigned short ifr_offset; +}; + +struct options { + const char *name; +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + const unsigned int flags:6; + const unsigned int arg_flags:10; +#else + const unsigned char flags; + const unsigned char arg_flags; +#endif + const unsigned short selector; +}; + +#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)}, +#ifdef SIOCSKEEPALIVE + {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)}, +#endif +#ifdef SIOCSOUTFILL + {"SIOCSOUTFILL", 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)}, +#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 */ +#endif + {"SIOCSIFADDR", 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}, +#ifdef SIOCSKEEPALIVE + {"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)} +}; + +/* + * 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); +#endif + +/* + * Our main function. + */ + +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 + 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 + unsigned int mask; + unsigned int did_flags; + unsigned int sai_hostname, sai_netmask; +#else + unsigned char mask; + unsigned char did_flags; +#endif + char *p; + char host[128]; + + goterr = 0; + did_flags = 0; +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + sai_hostname = 0; + sai_netmask = 0; +#endif + + /* skip argv[0] */ + ++argv; + --argc; + +#ifdef CONFIG_FEATURE_IFCONFIG_STATUS + if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) { + interface_opt_a = 1; + --argc; + ++argv; + } +#endif + + if (argc <= 1) { +#ifdef CONFIG_FEATURE_IFCONFIG_STATUS + return display_interfaces(argc ? *argv : NULL); +#else + bb_error_msg_and_die + ("ifconfig was not compiled with interface status display support."); +#endif + } + + /* Create a channel to the NET kernel. */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("socket"); + } + + /* get interface name */ + safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ); + + /* Process the remaining arguments. */ + while (*++argv != (char *) NULL) { + p = *argv; + mask = N_MASK; + if (*p == '-') { /* If the arg starts with '-'... */ + ++p; /* advance past it and */ + mask = M_MASK; /* set the appropriate mask. */ + } + 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;; + } + /* If we get here, there was a valid arg with an */ + /* invalid '-' prefix. */ + ++goterr; + goto LOOP; + } + } + + /* We fell through, so treat as possible hostname. */ + a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1; + mask = op->arg_flags; + goto HOSTNAME; + + FOUND_ARG: + if (mask & ARG_MASK) { + mask = op->arg_flags; + a1op = Arg1Opt + (op - OptArray); + if (mask & A_NETMASK & did_flags) { + bb_show_usage(); + } + if (*++argv == NULL) { + if (mask & A_ARG_REQ) { + bb_show_usage(); + } else { + --argv; + mask &= A_SET_AFTER; /* just for broadcast */ + } + } else { /* got an arg so process it */ + HOSTNAME: + did_flags |= (mask & (A_NETMASK|A_HOSTNAME)); + if (mask & A_CAST_HOST_COPY) { +#ifdef CONFIG_FEATURE_IFCONFIG_HW + if (mask & A_CAST_RESOLVE) { +#endif +#ifdef CONFIG_FEATURE_IPV6 + char *prefix; + int prefix_len = 0; +#endif + + safe_strncpy(host, *argv, (sizeof host)); +#ifdef CONFIG_FEATURE_IPV6 + if ((prefix = strchr(host, '/'))) { + if (safe_strtoi(prefix + 1, &prefix_len) || + (prefix_len < 0) || (prefix_len > 128)) + { + ++goterr; + goto LOOP; + } + *prefix = 0; + } +#endif + + sai.sin_family = AF_INET; + sai.sin_port = 0; + if (!strcmp(host, bb_INET_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)) { + /* + 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; + 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. */ + struct hostent *hp; + if ((hp = gethostbyname(host)) == (struct hostent *)NULL) { + ++goterr; + continue; + } + memcpy((char *) &sai.sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + } +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + if (mask & A_HOSTNAME) { + sai_hostname = sai.sin_addr.s_addr; + } + if (mask & A_NETMASK) { + sai_netmask = sai.sin_addr.s_addr; + } +#endif + p = (char *) &sai; +#ifdef CONFIG_FEATURE_IFCONFIG_HW + } else { /* A_CAST_HOST_COPY_IN_ETHER */ + /* This is the "hw" arg case. */ + if (strcmp("ether", *argv) || (*++argv == NULL)) { + bb_show_usage(); + } + safe_strncpy(host, *argv, (sizeof host)); + if (in_ether(host, &sa)) { + bb_error_msg("invalid hw-addr %s", host); + ++goterr; + continue; + } + p = (char *) &sa; + } +#endif + 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 + if (mask & A_MAP_TYPE) { + if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) { + ++goterr; + continue; + } + if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) { + *((unsigned char *) p) = i; + } else if (mask & A_MAP_USHORT) { + *((unsigned short *) p) = i; + } else { + *((unsigned long *) p) = i; + } + } else +#endif + if (mask & A_CAST_CHAR_PTR) { + *((caddr_t *) p) = (caddr_t) i; + } else { /* A_CAST_INT */ + *((int *) p) = i; + } + } + + if (ioctl(sockfd, a1op->selector, &ifr) < 0) { + perror(a1op->name); + ++goterr; + continue; + } +#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 + * + * Should really use regex.h here, not sure though how well + * 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 == ':') { + found_colon++; + } + } + + if (found_colon && *(ptr - 1) == '-') { + continue; + } + } +#endif + } + 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; +} + +#ifdef CONFIG_FEATURE_IFCONFIG_HW +/* Input an Ethernet address and convert to binary. */ +static int in_ether(char *bufp, struct sockaddr *sap) +{ + unsigned char *ptr; + int i, j; + unsigned char val; + unsigned char c; + + sap->sa_family = ARPHRD_ETHER; + ptr = sap->sa_data; + + i = 0; + do { + j = val = 0; + + /* We might get a semicolon here - not required. */ + if (i && (*bufp == ':')) { + bufp++; + } + + do { + c = *bufp; + if (((unsigned char)(c - '0')) <= 9) { + c -= '0'; + } else if (((unsigned char)((c|0x20) - 'a')) <= 5) { + c = (c|0x20) - ('a'-10); + } else if (j && (c == ':' || c == 0)) { + break; + } else { + return -1; + } + ++bufp; + val <<= 4; + val += c; + } while (++j < 2); + *ptr++ = val; + } while (++i < ETH_ALEN); + + return (int) (*bufp); /* Error if we don't end at end of string. */ +} +#endif diff --git a/busybox/networking/ifupdown.c b/busybox/networking/ifupdown.c new file mode 100644 index 0000000..1842be5 --- /dev/null +++ b/busybox/networking/ifupdown.c @@ -0,0 +1,1463 @@ +/* vi: set sw=4 ts=4: */ +/* + * ifupdown for busybox + * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au> + * 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> + * + * 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. + * + * 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. + */ + +/* TODO: standardise execute() return codes to return 0 for success and 1 for failure */ + +#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" + +#define MAX_OPT_DEPTH 10 +#define EUNBALBRACK 10001 +#define EUNDEFVAR 10002 +#define EUNBALPER 10000 + +#ifdef CONFIG_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 + +/* 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); +} + +struct method_t +{ + char *name; + command_set *up; + command_set *down; +}; + +struct address_family_t +{ + char *name; + int n_methods; + struct method_t *method; +}; + +struct mapping_defn_t +{ + struct mapping_defn_t *next; + + int max_matches; + int n_matches; + char **match; + + char *script; + + int max_mappings; + int n_mappings; + char **mapping; +}; + +struct variable_t +{ + char *name; + char *value; +}; + +struct interface_defn_t +{ + struct interface_defn_t *prev; + struct interface_defn_t *next; + + 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 +{ + llist_t *autointerfaces; + llist_t *ifaces; + struct mapping_defn_t *mappings; +}; + +static char no_act = 0; +static char verbose = 0; +static char **environ = NULL; + +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + +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 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 + +static void addstr(char **buf, size_t *len, size_t *pos, 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'; +} + +static int strncmpz(char *l, char *r, size_t llen) +{ + int i = strncmp(l, r, llen); + + if (i == 0) { + return(-r[llen]); + } else { + return(i); + } +} + +static char *get_var(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); + } + } + } + + return(NULL); +} + +static char *parse(char *command, struct interface_defn_t *ifd) +{ + + char *result = NULL; + size_t pos = 0, len = 0; + size_t old_pos[MAX_OPT_DEPTH] = { 0 }; + int okay[MAX_OPT_DEPTH] = { 1 }; + int opt_depth = 1; + + while (*command) { + switch (*command) { + + default: + addstr(&result, &len, &pos, 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] = pos; + okay[opt_depth] = 1; + opt_depth++; + command += 2; + } else { + addstr(&result, &len, &pos, "[", 1); + command++; + } + break; + case ']': + if (command[1] == ']' && opt_depth > 1) { + opt_depth--; + if (!okay[opt_depth]) { + pos = old_pos[opt_depth]; + result[pos] = '\0'; + } + command += 2; + } 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)); + command = nextpercent + 1; + break; + } + } +#endif + okay[opt_depth - 1] = 0; + } + + command = nextpercent + 1; + } + break; + } + } + + if (opt_depth > 1) { + errno = EUNBALBRACK; + free(result); + return(NULL); + } + + if (!okay[0]) { + errno = EUNDEFVAR; + free(result); + return(NULL); + } + + return(result); +} + +static int execute(char *command, struct interface_defn_t *ifd, execfn *exec) +{ + char *out; + int ret; + + out = parse(command, ifd); + if (!out) { + return(0); + } + ret = (*exec) (out); + + free(out); + if (ret != 1) { + return(0); + } + 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)); +} + +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 +static int loopback_up6(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + int result; + result =execute("ip addr add ::1 dev %iface%", ifd, exec); + result += execute("ip link set %iface% up", ifd, exec); + return ((result == 2) ? 2 : 0); +#else + 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)); +#else + 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 [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec); + 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% add %address%/%netmask%", ifd, exec); + result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec); +#endif + 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)); +#else + return(execute("ifconfig %iface% down", ifd, exec)); +#endif +} + +#ifdef CONFIG_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); + result += execute("ip link set %iface% up", ifd, exec); + 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)); +} +#endif + +static struct method_t methods6[] = { +#ifdef CONFIG_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 = { + "inet6", + sizeof(methods6) / sizeof(struct method_t), + methods6 +}; +#endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */ + +#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 +static int loopback_up(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + int result; + 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 == 2) ? 2 : 0); +#else + 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 + int result; + result = execute("ip addr flush dev %iface%", ifd, exec); + result += execute("ip link set %iface% down", ifd, exec); + return ((result == 2) ? 2 : 0); +#else + 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% [[peer %pointopoint%]] [[label %label%]]", ifd, exec); + result += execute("ip link set [[mtu %mtu%]] [[address %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", + ifd, exec); + result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec); + return ((result == 2) ? 2 : 0); +#endif +} + +static int static_down(struct interface_defn_t *ifd, execfn *exec) +{ + int result; +#ifdef CONFIG_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("ifconfig %iface% down", ifd, exec); +#endif + return ((result == 2) ? 2 : 0); +} + +static int execable(char *program) +{ + struct stat buf; + if (0 == stat(program, &buf)) { + if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode)) { + return(1); + } + } + return(0); +} + +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)); + } + return(0); +} + +static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) +{ + int result = 0; + if (execable("/sbin/udhcpc")) { + /* SIGUSR2 forces udhcpc to release the current lease and go inactive, + * and SIGTERM causes udhcpc to exit. Signals are queued and processed + * sequentially so we don't need to sleep */ + result = execute("kill -USR2 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); + result += execute("kill -TERM `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")) { + result = execute("kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", ifd, exec); + } else if (execable("/sbin/dhcpcd")) { + result = execute("dhcpcd -k %iface%", ifd, exec); + } + return (result || static_down(ifd, exec)); +} + +static int bootp_up(struct interface_defn_t *ifd, execfn *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)); +} + +static int ppp_down(struct interface_defn_t *ifd, execfn *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)); +} + +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)); +} + +static struct method_t methods[] = +{ + { "wvdial", wvdial_up, wvdial_down, }, + { "ppp", ppp_up, ppp_down, }, + { "static", static_up, static_down, }, + { "bootp", bootp_up, static_down, }, + { "dhcp", dhcp_up, dhcp_down, }, + { "loopback", loopback_up, loopback_down, }, +}; + +struct address_family_t addr_inet = +{ + "inet", + sizeof(methods) / sizeof(struct method_t), + methods +}; + +#endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */ + +static char *next_word(char **buf) +{ + unsigned short length; + char *word; + + if ((buf == NULL) || (*buf == NULL) || (**buf == '\0')) { + return NULL; + } + + /* Skip over leading whitespace */ + word = *buf; + while (isspace(*word)) { + ++word; + } + + /* Skip over comments */ + if (*word == '#') { + return(NULL); + } + + /* Find the length of this word */ + length = strcspn(word, " \t\n"); + if (length == 0) { + return(NULL); + } + *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) +{ + int i; + + for (i = 0; af[i]; i++) { + if (strcmp(af[i]->name, name) == 0) { + return af[i]; + } + } + return NULL; +} + +static struct method_t *get_method(struct address_family_t *af, char *name) +{ + int i; + + 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); +} + +static struct interfaces_file_t *read_interfaces(const char *filename) +{ +#ifdef CONFIG_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; + + 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) { + free(buf); + continue; /* blank 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); + } + + currmap->match[currmap->n_matches++] = bb_xstrdup(firstword); + } + currmap->max_mappings = 0; + 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; + } + 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, +#endif +#ifdef CONFIG_FEATURE_IFUPDOWN_IPX + &addr_ipx, +#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; + } + + 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\"", address_family_name); + return NULL; + } + + currif->method = get_method(currif->address_family, method_name); + if (!currif->method) { + bb_error_msg("unknown method \"%s\"", method_name); + 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); + } + currently_processing = IFACE; + } else if (strcmp(firstword, "auto") == 0) { + while ((firstword = next_word(&buf_ptr)) != NULL) { + + /* Check the interface isnt already listed */ + if (find_list_string(defn->autointerfaces, firstword)) { + 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); + } + 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; + } + 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; + } +#endif + break; + case NONE: + default: + bb_error_msg("misplaced option \"%s\"", buf); + return NULL; + } + } + free(buf); + } + if (ferror(f) != 0) { + bb_perror_msg_and_die("%s", filename); + } + fclose(f); + + return defn; +} + +static char *setlocalenv(char *format, char *name, 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); + + for (here = there = result; *there != '=' && *there; there++) { + if (*there == '-') + *there = '_'; + if (isalpha(*there)) + *there = toupper(*there); + + if (isalnum(*there) || *there == '_') { + *here = *there; + here++; + } + } + memmove(here, there, bb_strlen(there) + 1); + + return result; +} + +static void set_environ(struct interface_defn_t *iface, 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++) { + free(*ppch); + *ppch = NULL; + } + free(environ); + environ = NULL; + } + environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ )); + environend = environ; + *environend = NULL; + + 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) { + 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; +} + +static int doit(char *str) +{ + if (verbose || no_act) { + printf("%s\n", str); + } + if (!no_act) { + pid_t child; + int status; + + fflush(NULL); + switch (child = fork()) { + case -1: /* failure */ + return 0; + case 0: /* child */ + execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, environ); + exit(127); + } + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + return 0; + } + } + return (1); +} + +static int execute_all(struct interface_defn_t *ifd, execfn *exec, const char *opt) +{ + int i; + char *buf; + for (i = 0; i < ifd->n_options; i++) { + if (strcmp(ifd->option[i].name, opt) == 0) { + if (!(*exec) (ifd->option[i].value)) { + return 0; + } + } + } + + bb_xasprintf(&buf, "run-parts /etc/network/if-%s.d", opt); + if ((*exec)(buf) != 1) { + return 0; + } + return 1; +} + +static int check(char *str) { + return str != NULL; +} + +static int iface_up(struct interface_defn_t *iface) +{ + if (!iface->method->up(iface,check)) return -1; + set_environ(iface, "start"); + if (!execute_all(iface, doit, "pre-up")) return 0; + if (!iface->method->up(iface, doit)) return 0; + if (!execute_all(iface, doit, "up")) return 0; + return 1; +} + +static int iface_down(struct interface_defn_t *iface) +{ + if (!iface->method->down(iface,check)) return -1; + set_environ(iface, "stop"); + if (!execute_all(iface, doit, "down")) return 0; + if (!iface->method->down(iface, doit)) return 0; + if (!execute_all(iface, doit, "post-down")) return 0; + return 1; +} + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING +static int popen2(FILE **in, FILE **out, char *command, ...) +{ + va_list ap; + char *argv[11] = { command }; + int argc; + int infd[2], outfd[2]; + 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; + } + + 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; + } + /* unreached */ +} + +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); + + /* popen2() returns 0 on failure. */ + if (pid == 0) + return logical; + + /* 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); + + 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); + + if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) { + /* 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; + + 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); + } + } + + fclose(out); + + return logical; +} +#endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */ + +static llist_t *find_iface_state(llist_t *state_list, const char *iface) +{ + unsigned short iface_len = bb_strlen(iface); + llist_t *search = state_list; + + while (search) { + if ((strncmp(search->data, iface, iface_len) == 0) && + (search->data[iface_len] == '=')) { + return(search); + } + search = search->link; + } + return(NULL); +} + +extern int ifupdown_main(int argc, char **argv) +{ + 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; + const char *interfaces = "/etc/network/interfaces"; + const char *statefile = "/var/run/ifstate"; + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + int run_mappings = 1; +#endif + int do_all = 0; + int force = 0; + int any_failures = 0; + int i; + + if (bb_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 = 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; + } + } + + if (argc - optind > 0) { + if (do_all) { + bb_show_usage(); + } + } else { + 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"); + } + + /* 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 + } + } else { + target_list = llist_add_to_end(target_list, argv[optind]); + } + + + /* Update the interfaces */ + while (target_list) { + llist_t *iface_list; + struct interface_defn_t *currif; + char *iface; + char *liface; + char *pch; + int okay = 0; + int cmds_ret; + + iface = strdup(target_list->data); + target_list = target_list->link; + + pch = strchr(iface, '='); + if (pch) { + *pch = '\0'; + liface = strdup(pch + 1); + } else { + liface = strdup(iface); + } + + if (!force) { + const llist_t *iface_state = find_iface_state(state_list, iface); + + if (cmds == iface_up) { + /* ifup */ + if (iface_state) { + bb_error_msg("interface %s already configured", iface); + continue; + } + } else { + /* ifdown */ + if (iface_state) { + bb_error_msg("interface %s not configured", iface); + continue; + } + } + } + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + if ((cmds == iface_up) && run_mappings) { + struct mapping_defn_t *currmap; + + for (currmap = defn->mappings; currmap; currmap = currmap->next) { + + for (i = 0; i < currmap->n_matches; i++) { + if (fnmatch(currmap->match[i], liface, 0) != 0) + continue; + if (verbose) { + printf("Running mapping script %s on %s\n", currmap->script, liface); + } + liface = run_mapping(iface, currmap); + break; + } + } + } +#endif + + + iface_list = defn->ifaces; + while (iface_list) { + currif = (struct interface_defn_t *) iface_list->data; + if (strcmp(liface, currif->iface) == 0) { + char *oldiface = currif->iface; + + okay = 1; + currif->iface = iface; + + debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name); + + /* Call the cmds function pointer, does either iface_up() or iface_down() */ + 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 (!okay && !force) { + bb_error_msg("Ignoring unknown interface %s", liface); + any_failures += 1; + } else { + 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); + if (iface_state == NULL) { + state_list = 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; + } + } + } + } + } + + /* 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); + } + state_list = state_list->link; + } + fflush(state_fp); + } + + /* Cleanup */ + if (state_fp != NULL) { + fclose(state_fp); + state_fp = NULL; + } + + if (any_failures) + return 1; + return 0; +} diff --git a/busybox/networking/inetd.c b/busybox/networking/inetd.c new file mode 100644 index 0000000..169cc87 --- /dev/null +++ b/busybox/networking/inetd.c @@ -0,0 +1,1221 @@ +/* + * 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 + * + */ + +/* + * Inetd - Internet super-server + * + * This program invokes all internet services as needed. + * 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 + * 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''. + * + * 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 + * 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 + * protocol must be in /etc/protocols + * 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 unsupported + * + * Comment lines are indicated by a `#' in column 1. + */ + +/* + * Here's the scoop concerning the user.group feature: + * + * 1) No group listed. + * + * a) for root: NO setuid() or setgid() is done + * + * b) nonroot: setuid() + * setgid(primary group as found in passwd) + * initgroups(name, primary group) + * + * 2) set-group-option on. + * + * a) for root: NO setuid() + * setgid(specified group) + * setgroups(1, specified group) + * + * b) nonroot: setuid() + * setgid(specified group) + * initgroups(name, specified group) + * + * All supplementary groups are discarded at startup in case inetd was + * run manually. + */ + +#define __USE_BSD_SIGNAL + +#include "busybox.h" + + +#ifndef __linux__ +#ifndef RLIMIT_NOFILE +#define RLIMIT_NOFILE RLIMIT_OFILE +#endif +#endif + +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/un.h> +#include <sys/wait.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> + +#ifndef OPEN_MAX +#define OPEN_MAX 64 +#endif + +#define _PATH_INETDCONF "/etc/inetd.conf" +#define _PATH_INETDPID "/var/run/inetd.pid" + +#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 */ +#define MAXARGV 20 + +#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 + +/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ +#define FD_MARGIN (8) + +/* Check unsupporting builtin */ +#if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +# define INETD_FEATURE_ENABLED +#endif + +typedef struct servtab_s { + 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 */ +#ifdef INETD_FEATURE_ENABLED + const struct biltin *se_bi; /* if built-in, description */ +#endif + char *se_server; /* server program */ + 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 */ + 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_s *se_next; +} servtab_t; + +static servtab_t *servtab; + +#ifdef INETD_FEATURE_ENABLED +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, servtab_t *); /* fn which performs it */ +}; + + /* Echo received data */ +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_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, 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, 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, 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, servtab_t *); +static void chargen_dg(int, servtab_t *); +#endif + +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, }, +#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, }, +#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, }, +#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, }, +#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, }, +#endif + { NULL, 0, 0, 0, NULL } +}; +#endif /* INETD_FEATURE_ENABLED */ + +#ifdef RLIMIT_NOFILE +static struct rlimit rlim_ofile; +#endif + +/* Length of socket listen queue. Should be per-service probably. */ +static int global_queuelen = 128; + +static FILE *fconfig; +static sigset_t blockmask; +static sigset_t emptymask; +static fd_set allsock; +static int nsock; +static int maxsock; +static int timingout; +static int rlim_ofile_cur = OPEN_MAX; +static const char *CONFIG = _PATH_INETDCONF; + +static void +syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) + __attribute__ ((noreturn, format (printf, 2, 3))); + +static void +syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) +{ + 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); +} + +static char * inetd_strdup(const char *s) +{ + char *ms = strdup(s); + + if(ms == NULL) + syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m"); + return ms; +} + + +static servtab_t *getconfigent(void) +{ + static servtab_t serv; + servtab_t *sep = &serv; + int argc; + char *cp = NULL; + char *cp_ptr; + char *cp_ptr_ptr = NULL; + +more: + free(cp); + cp = bb_get_chomped_line_from_file(fconfig); + if (feof(fconfig)) { + free(cp); + return (NULL); + } + if ((cp == NULL) || (*cp == '#')) { + goto more; + } + /* make bind 0.0.0.0 and other zero default */ + memset((char *)sep, 0, sizeof *sep); + + cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* Error */ + goto more; + } + sep->se_service = inetd_strdup(cp_ptr); + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* Error */ + goto more; + } + if (strcmp(cp_ptr, "stream") == 0) + sep->se_socktype = SOCK_STREAM; + else if (strcmp(cp_ptr, "dgram") == 0) + sep->se_socktype = SOCK_DGRAM; + else if (strcmp(cp_ptr, "rdm") == 0) + sep->se_socktype = SOCK_RDM; + else if (strcmp(cp_ptr, "seqpacket") == 0) + sep->se_socktype = SOCK_SEQPACKET; + else if (strcmp(cp_ptr, "raw") == 0) + sep->se_socktype = SOCK_RAW; + else + sep->se_socktype = -1; + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + if (strcmp(cp_ptr, "unix") == 0) { + sep->se_family = AF_UNIX; + } else { + if (strncmp(cp_ptr, "rpc/", 4) == 0) { + syslog(LOG_ERR, "%s: rpc services not supported", + sep->se_service); + goto more; + } + sep->se_family = AF_INET; + } + sep->se_proto = inetd_strdup(cp_ptr); + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + { + char *s = strchr(cp_ptr, '.'); + if (s) { + *s++ = '\0'; + sep->se_max = atoi(s); + } else + sep->se_max = TOOMANY; + } + sep->se_wait = strcmp(cp_ptr, "wait") == 0; + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + + sep->se_user = inetd_strdup(cp_ptr); + { + char *cp_ptr2 = strchr(sep->se_user, '.'); + + if (cp_ptr2) { + *cp_ptr2++ = '\0'; + } + sep->se_group = cp_ptr2; + } + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + if (strcmp(cp_ptr, "internal") == 0) { +#ifdef INETD_FEATURE_ENABLED + 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; + } + sep->se_bi = bi; + sep->se_wait = bi->bi_wait; +#else + syslog(LOG_ERR, "internal service %s unknown", cp_ptr); + goto more; +#endif + } +#ifdef INETD_FEATURE_ENABLED + else { + sep->se_bi = NULL; + } +#endif + sep->se_server = inetd_strdup(cp_ptr); + + argc = 0; + while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) { + if (argc < MAXARGV) { + sep->se_argv[argc++] = inetd_strdup(cp_ptr); + } + } + free(cp); + + return (sep); +} + +static void freeconfig(servtab_t *cp) +{ + int i; + + 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); + for (i = 0; i < MAXARGV; i++) + free(cp->se_argv[i]); +} + +#ifdef INETD_FEATURE_ENABLED +static char **Argv; +static char *LastArg; + +static void setproctitle(char *a, int s) +{ + 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++ = ' '; +} +#endif /* INETD_FEATURE_ENABLED */ + + +static void setup(servtab_t *sep) +{ + int on = 1; + + 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; + } + 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_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) { +#ifdef RLIMIT_NOFILE +# define FD_CHUNK 32 + struct rlimit rl; + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { + syslog(LOG_ERR, "getrlimit: %m"); + return; + } + rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? 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", +# else + "bump_nofile: cannot extend file limit, max = %ld", +# endif + rl.rlim_cur); + return; + } + + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { + syslog(LOG_ERR, "setrlimit: %m"); + return; + } + + rlim_ofile_cur = rl.rlim_cur; + return; +#else + syslog(LOG_ERR, "bump_nofile: cannot extend file limit"); + return; +#endif /* RLIMIT_NOFILE */ + } + } +} + +static void config(int signum) +{ + servtab_t *sep, *cp, **sepp; + sigset_t oldmask; + unsigned n; + + (void)signum; + + if (fconfig != NULL) { + fseek(fconfig, 0L, L_SET); + } else { + fconfig = fopen(CONFIG, "r"); + if (fconfig == NULL) { + syslog(LOG_ERR, "%s: %m", CONFIG); + return; + } + } + + for (sep = servtab; 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) + break; + if (sep != 0) { + 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 ( +#ifdef INETD_FEATURE_ENABLED + cp->se_bi == 0 && +#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); + for (i = 0; i < MAXARGV; i++) + SWAP(char *, sep->se_argv[i], cp->se_argv[i]); +#undef SWAP + sigprocmask(SIG_SETMASK, &oldmask, NULL); + // This freeconfig() is probably a bug, since it will try and free() + // each of the argv[] values, which are really just pointers + // into the middle of a single line buffer for the config file. + //freeconfig(cp); // BUG? + } else { + sep = (servtab_t *)xmalloc(sizeof (*sep)); + *sep = *cp; + sep->se_fd = -1; + sigprocmask(SIG_BLOCK, &blockmask, &oldmask); + sep->se_next = servtab; + servtab = sep; + sigprocmask(SIG_SETMASK, &oldmask, NULL); + } + sep->se_checked = 1; + + switch (sep->se_family) { + 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); + break; + case AF_INET: + sep->se_ctrladdr_in.sin_family = AF_INET; + sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; + { + u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0); + + if (port == 0) { + syslog(LOG_ERR, + "%s/%s: unknown service", + sep->se_service, sep->se_proto); + continue; + } + 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); + } + sep->se_fd = -1; + } + if (sep->se_fd == -1) + setup(sep); + } + } + } + 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_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); + } + if (sep->se_family == AF_UNIX) + (void)unlink(sep->se_service); + freeconfig(sep); + free((char *)sep); + } + sigprocmask(SIG_SETMASK, &oldmask, NULL); +} + + + +static void reapchild(int signum) +{ + int status; + int pid; + servtab_t *sep; + + (void)signum; + for (;;) { + pid = wait3(&status, WNOHANG, (struct rusage *)0); + 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++; + } + } +} + +static void retry(int signum) +{ + servtab_t *sep; + + (void)signum; + timingout = 0; + for (sep = servtab; sep; sep = sep->se_next) { + if (sep->se_fd == -1) { + switch (sep->se_family) { + case AF_UNIX: + case AF_INET: + setup(sep); + break; + } + } + } +} + +static void goaway(int signum) +{ + servtab_t *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); +} + + + +extern int inetd_main(int argc, char *argv[]) +{ + servtab_t *sep; + struct group *grp = NULL; + struct sigaction sa; + int pid; + unsigned long opt; + char *sq; + gid_t gid; + +#ifdef INETD_FEATURE_ENABLED + extern char **environ; +#endif + + gid = getgid(); + setgroups(1, &gid); + +#ifdef INETD_FEATURE_ENABLED + Argv = argv; + if (environ == 0 || *environ == 0) + environ = argv; + while (*environ) + environ++; + LastArg = environ[-1] + strlen(environ[-1]); +#endif + +#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 */ + + if(opt & 1) { + global_queuelen = atoi(sq); + if (global_queuelen < 8) global_queuelen=8; + } + argc -= optind; + 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); + } + } + +#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; + } +#endif + + config(0); + + sigemptyset(&emptymask); + sigemptyset(&blockmask); + sigaddset(&blockmask, SIGCHLD); + sigaddset(&blockmask, SIGHUP); + sigaddset(&blockmask, SIGALRM); + + 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); + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + { + /* 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); + } + + for (;;) { + fd_set readable; + int ctrl; + int n; + + if (nsock == 0) { + sigprocmask(SIG_BLOCK, &blockmask, NULL); + while (nsock == 0) { + sigsuspend(&emptymask); + } + sigprocmask(SIG_SETMASK, &emptymask, NULL); + } + readable = allsock; + n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0); + if (n <= 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; + } + } else { + ctrl = sep->se_fd; + } + sigprocmask(SIG_BLOCK, &blockmask, NULL); + pid = 0; +#ifdef INETD_FEATURE_ENABLED + if (sep->se_bi == 0 || sep->se_bi->bi_fork) +#endif + { + if (sep->se_count++ == 0) { + gettimeofday(&sep->se_time, (struct timezone *)0); + } + else if (sep->se_count >= sep->se_max) { + struct timeval now; + + 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); + close(sep->se_fd); + sep->se_fd = -1; + sep->se_count = 0; + nsock--; + sigprocmask(SIG_SETMASK, &emptymask, NULL); + if (!timingout) { + timingout = 1; + alarm(RETRYTIME); + } + 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) { +#ifdef INETD_FEATURE_ENABLED + if (sep->se_bi) { + (*sep->se_bi->bi_fn)(ctrl, sep); + } else +#endif + { + struct passwd *pwd = getpwnam(sep->se_user); + if (pwd == NULL) { + syslog_err_and_discard_dg( + sep->se_socktype, + "getpwnam: %s: No such user", + sep->se_user); + } + 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); + } + /* + * 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); + } + 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"); + } + } +#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); + } + } + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { + close(ctrl); + } + } + } + } +} + + +/* + * 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, servtab_t *sep) +{ + 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); +} + +/* Echo service -- echo data back */ +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)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD +/* Discard service -- ignore data */ +static void discard_stream(int s, servtab_t *sep) +{ + char buffer[BUFSIZE]; + + setproctitle(sep->se_service, s); + while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || + errno == EINTR) + ; + exit(0); +} + +/* Discard service -- ignore data */ +static void discard_dg(int s, servtab_t *sep) +{ + char buffer[BUFSIZE]; + (void)sep; + read(s, buffer, sizeof(buffer)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +#include <ctype.h> +#define LINESIZ 72 +static char ring[128]; +static char *endring; + +static void initring(void) +{ + int i; + + endring = ring; + + for (i = 0; i <= 128; ++i) + if (isprint(i)) + *endring++ = i; +} + +/* Character generator */ +static void chargen_stream(int s, servtab_t *sep) +{ + char *rs; + int len; + char text[LINESIZ+2]; + + setproctitle(sep->se_service, s); + + if (!endring) { + initring(); + rs = ring; + } + + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + for (rs = ring;;) { + if ((len = endring - rs) >= LINESIZ) + memcpy(rs, text, LINESIZ); + else { + memcpy(rs, text, len); + memcpy(ring, text + len, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + if (write(s, text, sizeof(text)) != sizeof(text)) + break; + } + exit(0); +} + +/* Character generator */ +static void chargen_dg(int s, servtab_t *sep) +{ + struct sockaddr sa; + static char *rs; + size_t len, size; + char text[LINESIZ+2]; + + (void)sep; + + if (endring == 0) { + initring(); + rs = ring; + } + + size = sizeof(sa); + if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) + return; + + if ((len = endring - rs) >= LINESIZ) + memcpy(rs, text, LINESIZ); + else { + memcpy(rs, text, len); + memcpy(ring, text + len, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME +/* + * Return a machine readable date and time, in the form of the + * number of seconds since midnight, Jan 1, 1900. Since gettimeofday + * returns the number of seconds since midnight, Jan 1, 1970, + * we must add 2208988800 seconds to this figure to make up for + * some seventy years Bell Labs was asleep. + */ + +static long 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)); +} + +static void machtime_stream(int s, servtab_t *sep) +{ + long result; + (void)sep; + + result = machtime(); + write(s, (char *) &result, sizeof(result)); +} + +static void machtime_dg(int s, servtab_t *sep) +{ + long result; + struct sockaddr sa; + size_t size; + (void)sep; + + size = sizeof(sa); + if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) + return; + result = machtime(); + (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME +/* Return human-readable time of day */ +static int human_readable_time_sprintf(char *buffer) +{ + time_t clocc = time(NULL); + + return sprintf(buffer, "%.24s\r\n", ctime(&clocc)); +} + +static void daytime_stream(int s, servtab_t *sep) +{ + char buffer[256]; + size_t st = human_readable_time_sprintf(buffer); + + (void)sep; + + write(s, buffer, st); +} + +/* Return human-readable time of day */ +static void daytime_dg(int s, servtab_t *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) + return; + size = human_readable_time_sprintf(buffer); + sendto(s, buffer, size, 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */ diff --git a/busybox/networking/ip.c b/busybox/networking/ip.c new file mode 100644 index 0000000..cfdbf8b --- /dev/null +++ b/busybox/networking/ip.c @@ -0,0 +1,115 @@ +/* + * 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 <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 "./libiproute/utils.h" +#include "./libiproute/ip_common.h" + +#include "busybox.h" + +#if 0 +int preferred_family = AF_UNSPEC; +int oneline = 0; +char * _SL_ = NULL; + +void ip_parse_common_args(int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (argc > 1) { + char *opt = argv[1]; + + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + + if (opt[0] != '-') + break; + + if (opt[1] == '-') + opt++; + + 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" ; +} +#endif + +int ip_main(int argc, char **argv) +{ + int ret = EXIT_FAILURE; + + ip_parse_common_args(&argc, &argv); + + if (argc > 1) { +#ifdef CONFIG_FEATURE_IP_ADDRESS + if (matches(argv[1], "address") == 0) { + ret = do_ipaddr(argc-2, argv+2); + } +#endif +#ifdef CONFIG_FEATURE_IP_ROUTE + if (matches(argv[1], "route") == 0) { + ret = do_iproute(argc-2, argv+2); + } +#endif +#ifdef CONFIG_FEATURE_IP_LINK + if (matches(argv[1], "link") == 0) { + ret = do_iplink(argc-2, argv+2); + } +#endif +#ifdef CONFIG_FEATURE_IP_TUNNEL + if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) { + ret = do_iptunnel(argc-2, argv+2); + } +#endif + } + if (ret) { + bb_show_usage(); + } + return(EXIT_SUCCESS); +} diff --git a/busybox/networking/ipaddr.c b/busybox/networking/ipaddr.c new file mode 100644 index 0000000..7826194 --- /dev/null +++ b/busybox/networking/ipaddr.c @@ -0,0 +1,27 @@ +/* + * 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/busybox/networking/ipcalc.c b/busybox/networking/ipcalc.c new file mode 100644 index 0000000..bcd272b --- /dev/null +++ b/busybox/networking/ipcalc.c @@ -0,0 +1,224 @@ +/* vi: set sw=4 ts=4 ai: */ +/* + * Mini ipcalc implementation for busybox + * + * By Jordan Crouse <jordan@cosmicpenguin.net> + * Stephan Linz <linz@li-pro.net> + * + * 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 + */ + +#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;} + +#define CLASS_A_NETMASK ntohl(0xFF000000) +#define CLASS_B_NETMASK ntohl(0xFFFF0000) +#define CLASS_C_NETMASK ntohl(0xFFFFFF00) + +static unsigned long get_netmask(unsigned long ipaddr) +{ + ipaddr = htonl(ipaddr); + + if ((ipaddr & 0xC0000000) == 0xC0000000) + return CLASS_C_NETMASK; + else if ((ipaddr & 0x80000000) == 0x80000000) + return CLASS_B_NETMASK; + else if ((ipaddr & 0x80000000) == 0) + return CLASS_A_NETMASK; + else + return 0; +} + +#ifdef CONFIG_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; +} +#endif + +#define NETMASK 0x01 +#define BROADCAST 0x02 +#define NETWORK 0x04 +#define NETPREFIX 0x08 +#define HOSTNAME 0x10 +#define SILENT 0x20 + + +int ipcalc_main(int argc, char **argv) +{ + unsigned long mode; + + in_addr_t netmask; + in_addr_t broadcast; + in_addr_t network; + in_addr_t ipaddr; + struct in_addr a; + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + unsigned long netprefix = 0; + int have_netmask = 0; + char *ipstr, *prefixstr; +#endif + + static const struct option long_options[] = { + {"netmask", no_argument, NULL, 'm'}, + {"broadcast", no_argument, NULL, 'b'}, + {"network", no_argument, NULL, 'n'}, +#ifdef CONFIG_FEATURE_IPCALC_FANCY + {"prefix", no_argument, NULL, 'p'}, + {"hostname", no_argument, NULL, 'h'}, + {"silent", no_argument, NULL, 's'}, +#endif + {NULL, 0, NULL, 0} + }; + + bb_applet_long_options = long_options; + mode = bb_getopt_ulflags(argc, argv, +#ifdef CONFIG_FEATURE_IPCALC_FANCY + "mbnphs" +#else + "mbn" +#endif + ); + if (mode & (BROADCAST | NETWORK | NETPREFIX)) { + if (argc - optind > 2) { + bb_show_usage(); + } + } else { + if (argc - optind != 1) { + bb_show_usage(); + } + } + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + prefixstr = ipstr = argv[optind]; + + while(*prefixstr) { + if (*prefixstr == '/') { + *prefixstr = (char)0; + prefixstr++; + if (*prefixstr) { + unsigned int msk; + + if (safe_strtoul(prefixstr, &netprefix) || netprefix > 32) { + IPCALC_MSG(bb_error_msg_and_die("bad IP prefix: %s\n", prefixstr), + exit(EXIT_FAILURE)); + } + 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); +#else + ipaddr = inet_aton(argv[optind], &a); +#endif + + if (ipaddr == 0) { + IPCALC_MSG(bb_error_msg_and_die("bad IP address: %s", argv[optind]), + exit(EXIT_FAILURE)); + } + ipaddr = a.s_addr; + + if (argc - optind == 2) { +#ifdef CONFIG_FEATURE_IPCALC_FANCY + if (have_netmask) { + IPCALC_MSG(bb_error_msg_and_die("Both prefix and netmask were specified, use one or the other.\n"), + exit(EXIT_FAILURE)); + } + +#endif + netmask = inet_aton(argv[optind + 1], &a); + if (netmask == 0) { + IPCALC_MSG(bb_error_msg_and_die("bad netmask: %s", argv[optind + 1]), + exit(EXIT_FAILURE)); + } + netmask = a.s_addr; + } else { +#ifdef CONFIG_FEATURE_IPCALC_FANCY + + if (!have_netmask) +#endif + /* JHC - If the netmask wasn't provided then calculate it */ + netmask = get_netmask(ipaddr); + } + + if (mode & NETMASK) { + printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask))); + } + + if (mode & BROADCAST) { + broadcast = (ipaddr & netmask) | ~netmask; + printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast))); + } + + if (mode & NETWORK) { + network = ipaddr & netmask; + printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network))); + } + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + if (mode & NETPREFIX) { + printf("PREFIX=%i\n", get_prefix(netmask)); + } + + 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]); + } + + 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/busybox/networking/iplink.c b/busybox/networking/iplink.c new file mode 100644 index 0000000..7207e17 --- /dev/null +++ b/busybox/networking/iplink.c @@ -0,0 +1,27 @@ +/* + * 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/busybox/networking/iproute.c b/busybox/networking/iproute.c new file mode 100644 index 0000000..d049a87 --- /dev/null +++ b/busybox/networking/iproute.c @@ -0,0 +1,27 @@ +/* + * 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/busybox/networking/iptunnel.c b/busybox/networking/iptunnel.c new file mode 100644 index 0000000..f2b2e8a --- /dev/null +++ b/busybox/networking/iptunnel.c @@ -0,0 +1,27 @@ +/* + * 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/busybox/networking/libiproute/Makefile b/busybox/networking/libiproute/Makefile new file mode 100644 index 0000000..d3aefaa --- /dev/null +++ b/busybox/networking/libiproute/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/networking/libiproute +LIBIPROUTE_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/networking/libiproute/Makefile.in b/busybox/networking/libiproute/Makefile.in new file mode 100644 index 0000000..fcc7f48 --- /dev/null +++ b/busybox/networking/libiproute/Makefile.in @@ -0,0 +1,84 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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:=$(top_builddir)/networking/libiproute/ +endif +srcdir=$(top_srcdir)/networking/libiproute + +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 + +LIBIPROUTE-$(CONFIG_IPADDR) += \ + ip_parse_common_args.o \ + ipaddress.o \ + libnetlink.o \ + ll_addr.o \ + ll_map.o \ + ll_types.o \ + rt_names.o \ + utils.o + +LIBIPROUTE-$(CONFIG_IPLINK) += \ + 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 + +LIBIPROUTE-$(CONFIG_IPROUTE) += \ + ip_parse_common_args.o \ + iproute.o \ + libnetlink.o \ + ll_map.o \ + rt_names.o \ + rtm_map.o \ + utils.o + +LIBIPROUTE-$(CONFIG_IPTUNNEL) += \ + ip_parse_common_args.o \ + iptunnel.o \ + rt_names.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)) + +$(LIBIPROUTE_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/networking/libiproute/ip_common.h b/busybox/networking/libiproute/ip_common.h new file mode 100644 index 0000000..25e9c6c8 --- /dev/null +++ b/busybox/networking/libiproute/ip_common.h @@ -0,0 +1,18 @@ +extern int preferred_family; +extern char * _SL_; + +extern void ip_parse_common_args(int *argcp, char ***argvp); +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 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); diff --git a/busybox/networking/libiproute/ip_parse_common_args.c b/busybox/networking/libiproute/ip_parse_common_args.c new file mode 100644 index 0000000..a76df48 --- /dev/null +++ b/busybox/networking/libiproute/ip_parse_common_args.c @@ -0,0 +1,76 @@ +/* + * 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 <string.h> + +#include "utils.h" +#include "ip_common.h" + +#include "busybox.h" + +int preferred_family = AF_UNSPEC; +int oneline = 0; +char * _SL_ = NULL; + +void ip_parse_common_args(int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (argc > 1) { + char *opt = argv[1]; + + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + + if (opt[0] != '-') + break; + + if (opt[1] == '-') + opt++; + + if (matches(opt, "-family") == 0) { + argc--; + 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; + } else { + bb_show_usage(); + } + argc--; argv++; + } + _SL_ = oneline ? "\\" : "\n" ; + *argcp = argc; + *argvp = argv; +} diff --git a/busybox/networking/libiproute/ipaddress.c b/busybox/networking/libiproute/ipaddress.c new file mode 100644 index 0000000..7e0c757 --- /dev/null +++ b/busybox/networking/libiproute/ipaddress.c @@ -0,0 +1,825 @@ +/* + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Changes: + * 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 "rt_names.h" +#include "utils.h" + +#include "libbb.h" + +static struct +{ + int ifindex; + int family; + int oneline; + int showqueue; + inet_prefix pfx; + int scope, scopemask; + int flags, flagmask; + int up; + char *label; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} filter; + +void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) +{ + fprintf(fp, "<"); + 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); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); +#endif + _PF(UP); +#undef _PF + if (flags) + fprintf(fp, "%x", flags); + if (mdown) + fprintf(fp, ",M-DOWN"); + fprintf(fp, "> "); +} + +static void print_queuelen(char *name) +{ + struct ifreq ifr; + int s; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { + perror("SIOCGIFXQLEN"); + close(s); + return; + } + close(s); + + if (ifr.ifr_qlen) + printf("qlen %d", ifr.ifr_qlen); +} + +static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr * tb[IFLA_MAX+1]; + int len = n->nlmsg_len; + unsigned m_flag = 0; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (filter.ifindex && ifi->ifi_index != filter.ifindex) + return 0; + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + return 0; + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_IFNAME] == NULL) { + 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)) + return 0; + + if (n->nlmsg_type == RTM_DELLINK) + fprintf(fp, "Deleted "); + + fprintf(fp, "%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: "); + else { + fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); + m_flag = ll_index_to_flags(iflink); + m_flag = !(m_flag & IFF_UP); + } + } else { + fprintf(fp, ": "); + } + print_link_flags(fp, ifi->ifi_flags, m_flag); + + if (tb[IFLA_MTU]) + fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); + if (tb[IFLA_QDISC]) + fprintf(fp, "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)); + } +#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))); + + if (tb[IFLA_ADDRESS]) { + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + if (tb[IFLA_BROADCAST]) { + if (ifi->ifi_flags&IFF_POINTOPOINT) + fprintf(fp, " peer "); + else + fprintf(fp, " brd "); + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), + RTA_PAYLOAD(tb[IFLA_BROADCAST]), + ifi->ifi_type, + b1, sizeof(b1))); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * rta_tb[IFA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) + return 0; + len -= NLMSG_LENGTH(sizeof(*ifa)); + if (len < 0) { + bb_error_msg("wrong nlmsg len %d", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) + return 0; + + memset(rta_tb, 0, sizeof(rta_tb)); + parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + + if (!rta_tb[IFA_LOCAL]) + rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; + if (!rta_tb[IFA_ADDRESS]) + rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; + + if (filter.ifindex && filter.ifindex != ifa->ifa_index) + return 0; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + return 0; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + return 0; + if (filter.label) { + const char *label; + if (rta_tb[IFA_LABEL]) + label = RTA_DATA(rta_tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + return 0; + } + if (filter.pfx.family) { + if (rta_tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + return 0; + } + } + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELADDR; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + return 0; + } + + if (n->nlmsg_type == RTM_DELADDR) + fprintf(fp, "Deleted "); + + if (filter.oneline) + fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); + if (ifa->ifa_family == AF_INET) + fprintf(fp, " inet "); + else if (ifa->ifa_family == AF_INET6) + fprintf(fp, " inet6 "); + else + fprintf(fp, " 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]), + RTA_DATA(rta_tb[IFA_LOCAL]), + abuf, sizeof(abuf))); + + 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); + } else { + fprintf(fp, " 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); + } + } + + if (rta_tb[IFA_BROADCAST]) { + fprintf(fp, "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 ", + 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) { + ifa->ifa_flags &= ~IFA_F_SECONDARY; + fprintf(fp, "secondary "); + } + if (ifa->ifa_flags&IFA_F_TENTATIVE) { + ifa->ifa_flags &= ~IFA_F_TENTATIVE; + fprintf(fp, "tentative "); + } + if (ifa->ifa_flags&IFA_F_DEPRECATED) { + ifa->ifa_flags &= ~IFA_F_DEPRECATED; + fprintf(fp, "deprecated "); + } + if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { + fprintf(fp, "dynamic "); + } else + ifa->ifa_flags &= ~IFA_F_PERMANENT; + if (ifa->ifa_flags) + fprintf(fp, "flags %02x ", ifa->ifa_flags); + if (rta_tb[IFA_LABEL]) + fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); + if (rta_tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + char buf[128]; + fprintf(fp, "%s", _SL_); + if (ci->ifa_valid == 0xFFFFFFFFU) + sprintf(buf, "valid_lft forever"); + else + sprintf(buf, "valid_lft %dsec", ci->ifa_valid); + if (ci->ifa_prefered == 0xFFFFFFFFU) + sprintf(buf+strlen(buf), " preferred_lft forever"); + else + sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); + fprintf(fp, " %s", buf); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +struct nlmsg_list +{ + struct nlmsg_list *next; + struct nlmsghdr h; +}; + +static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp) +{ + for ( ;ainfo ; ainfo = ainfo->next) { + struct nlmsghdr *n = &ainfo->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (n->nlmsg_type != RTM_NEWADDR) + continue; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) + return -1; + + if (ifa->ifa_index != ifindex || + (filter.family && filter.family != ifa->ifa_family)) + continue; + + print_addrinfo(NULL, n, fp); + } + return 0; +} + + +static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + struct nlmsg_list **linfo = (struct nlmsg_list**)arg; + struct nlmsg_list *h; + struct nlmsg_list **lp; + + h = malloc(n->nlmsg_len+sizeof(void*)); + if (h == NULL) + return -1; + + memcpy(&h->h, n, n->nlmsg_len); + h->next = NULL; + + for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */; + *lp = h; + + ll_remember_index(who, n, NULL); + return 0; +} + +static void ipaddr_reset_filter(int _oneline) +{ + memset(&filter, 0, sizeof(filter)); + filter.oneline = _oneline; +} + +extern int ipaddr_list_or_flush(int argc, char **argv, int flush) +{ + const char *option[] = { "to", "scope", "up", "label", "dev", 0 }; + struct nlmsg_list *linfo = NULL; + struct nlmsg_list *ainfo = NULL; + struct nlmsg_list *l; + struct rtnl_handle rth; + char *filter_dev = NULL; + int no_link = 0; + + ipaddr_reset_filter(oneline); + filter.showqueue = 1; + + if (filter.family == AF_UNSPEC) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + if (filter.family == AF_PACKET) { + fprintf(stderr, "Cannot flush link addresses.\n"); + return -1; + } + } + + while (argc > 0) { + const unsigned short option_num = compare_string_array(option, *argv); + switch (option_num) { + case 0: /* to */ + NEXT_ARG(); + get_prefix(&filter.pfx, *argv, filter.family); + if (filter.family == AF_UNSPEC) { + filter.family = filter.pfx.family; + } + break; + case 1: /* scope */ + { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) { + invarg("invalid \"scope\"\n", *argv); + } + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + break; + } + case 2: /* up */ + filter.up = 1; + break; + case 3: /* label */ + NEXT_ARG(); + filter.label = *argv; + break; + case 4: /* dev */ + NEXT_ARG(); + default: + if (filter_dev) { + duparg2("dev", *argv); + } + filter_dev = *argv; + } + argv++; + argc--; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + 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"); + } + + 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; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + 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); + } + } + + 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"); + } + } + + + if (filter.family && filter.family != AF_PACKET) { + struct nlmsg_list **lp; + lp=&linfo; + + if (filter.oneline) + no_link = 1; + + 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) { + struct nlmsghdr *n = &a->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (ifa->ifa_index != ifi->ifi_index || + (filter.family && filter.family != ifa->ifa_family)) + continue; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + continue; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + continue; + if (filter.pfx.family || filter.label) { + struct rtattr *tb[IFA_MAX+1]; + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); + if (!tb[IFA_LOCAL]) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + + if (filter.pfx.family && tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + continue; + } + if (filter.label) { + SPRINT_BUF(b1); + const char *label; + if (tb[IFA_LABEL]) + label = RTA_DATA(tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + continue; + } + } + + ok = 1; + break; + } + if (!ok) + *lp = l->next; + else + lp = &l->next; + } + } + + for (l=linfo; l; l = l->next) { + if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + if (filter.family != AF_PACKET) + print_selected_addrinfo(ifi->ifi_index, ainfo, stdout); + } + fflush(stdout); + } + + exit(0); +} + +static int default_scope(inet_prefix *lcl) +{ + if (lcl->family == AF_INET) { + if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) + return RT_SCOPE_HOST; + } + return 0; +} + +static int ipaddr_modify(int cmd, int argc, char **argv) +{ + const char *option[] = { "peer", "remote", "broadcast", "brd", + "anycast", "scope", "dev", "label", "local", 0 }; + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ifaddrmsg ifa; + char buf[256]; + } req; + 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; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.ifa.ifa_family = preferred_family; + + while (argc > 0) { + const unsigned short option_num = compare_string_array(option, *argv); + switch (option_num) { + case 0: /* peer */ + case 1: /* remote */ + NEXT_ARG(); + + if (peer_len) { + duparg("peer", *argv); + } + get_prefix(&peer, *argv, req.ifa.ifa_family); + peer_len = peer.bytelen; + if (req.ifa.ifa_family == AF_UNSPEC) { + req.ifa.ifa_family = peer.family; + } + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); + req.ifa.ifa_prefixlen = peer.bitlen; + break; + case 2: /* broadcast */ + case 3: /* brd */ + { + inet_prefix addr; + NEXT_ARG(); + if (brd_len) { + duparg("broadcast", *argv); + } + if (strcmp(*argv, "+") == 0) { + brd_len = -1; + } + else if (strcmp(*argv, "-") == 0) { + brd_len = -2; + } else { + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); + brd_len = addr.bytelen; + } + break; + } + case 4: /* anycast */ + { + inet_prefix addr; + NEXT_ARG(); + if (any_len) { + duparg("anycast", *argv); + } + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) { + req.ifa.ifa_family = addr.family; + } + addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); + any_len = addr.bytelen; + break; + } + case 5: /* scope */ + { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) { + invarg(*argv, "invalid scope value."); + } + req.ifa.ifa_scope = scope; + scoped = 1; + break; + } + case 6: /* dev */ + NEXT_ARG(); + d = *argv; + break; + case 7: /* label */ + NEXT_ARG(); + l = *argv; + addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); + break; + case 8: /* local */ + NEXT_ARG(); + default: + if (local_len) { + duparg2("local", *argv); + } + get_prefix(&lcl, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) { + req.ifa.ifa_family = lcl.family; + } + 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."); + return -1; + } + if (l && matches(d, l) != 0) { + bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l); + } + + if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { + peer = lcl; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); + } + if (req.ifa.ifa_prefixlen == 0) + req.ifa.ifa_prefixlen = lcl.bitlen; + + if (brd_len < 0 && cmd != RTM_DELADDR) { + 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; + } + brd = peer; + if (brd.bitlen <= 30) { + for (i=31; i>=brd.bitlen; i--) { + if (brd_len == -1) + brd.data[0] |= htonl(1<<(31-i)); + else + brd.data[0] &= ~htonl(1<<(31-i)); + } + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); + brd_len = brd.bytelen; + } + } + if (!scoped && cmd != RTM_DELADDR) + req.ifa.ifa_scope = default_scope(&lcl); + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + 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; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + exit(0); +} + +extern int do_ipaddr(int argc, char **argv) +{ + const char *commands[] = { "add", "delete", "list", "show", "lst", "flush", 0 }; + unsigned short command_num = 2; + + 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); +} diff --git a/busybox/networking/libiproute/iplink.c b/busybox/networking/libiproute/iplink.c new file mode 100644 index 0000000..2550c19 --- /dev/null +++ b/busybox/networking/libiproute/iplink.c @@ -0,0 +1,367 @@ +/* + * 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> + * + */ + +#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 <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 "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#include "libbb.h" + + +/* take 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; +} + +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; +} + +static int do_chflags(char *dev, __u32 flags, __u32 mask) +{ + struct ifreq ifr; + int fd; + int err; + + strcpy(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) { + ifr.ifr_flags &= ~mask; + ifr.ifr_flags |= mask&flags; + err = ioctl(fd, SIOCSIFFLAGS, &ifr); + if (err) + perror("SIOCSIFFLAGS"); + } + close(fd); + return err; +} + +static int 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); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCSIFNAME, &ifr); + if (err) { + perror("SIOCSIFNAME"); + close(fd); + return -1; + } + close(fd); + return err; +#endif + return 0; +} + +static int 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; + } + close(s); + + return 0; +} + +static int 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; + } + close(s); + + return 0; +} + +static int get_address(char *dev, int *htype) +{ + struct ifreq ifr; + struct sockaddr_ll me; + int alen; + int s; + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_PACKET)"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, dev); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + close(s); + return -1; + } + + 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; + } + + alen = sizeof(me); + if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { + perror("getsockname"); + close(s); + return -1; + } + 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) +{ + int alen; + + memset(ifr, 0, sizeof(*ifr)); + strcpy(ifr->ifr_name, dev); + ifr->ifr_hwaddr.sa_family = hatype; + alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); + if (alen < 0) + return -1; + if (alen != halen) { + bb_error_msg("Wrong address (%s) length: expected %d bytes", lla, halen); + return -1; + } + return 0; +} + +static int 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; + } + close(s); + return 0; +} + + +static int do_set(int argc, char **argv) +{ + char *dev = NULL; + __u32 mask = 0; + __u32 flags = 0; + int qlen = -1; + int mtu = -1; + char *newaddr = NULL; + char *newbrd = NULL; + struct ifreq ifr0, ifr1; + char *newname = NULL; + int htype, halen; + + while (argc > 0) { + if (strcmp(*argv, "up") == 0) { + mask |= IFF_UP; + flags |= IFF_UP; + } else if (strcmp(*argv, "down") == 0) { + mask |= IFF_UP; + flags &= ~IFF_UP; + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + newname = *argv; + } else if (strcmp(*argv, "mtu") == 0) { + 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) { + NEXT_ARG(); + mask |= IFF_MULTICAST; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_MULTICAST; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_MULTICAST; + } else + return on_off("multicast"); + } else if (strcmp(*argv, "arp") == 0) { + NEXT_ARG(); + mask |= IFF_NOARP; + if (strcmp(*argv, "on") == 0) { + flags &= ~IFF_NOARP; + } else if (strcmp(*argv, "off") == 0) { + flags |= IFF_NOARP; + } else + return on_off("noarp"); + } else { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (dev) + duparg2("dev", *argv); + dev = *argv; + } + argc--; argv++; + } + + if (!dev) { + bb_error_msg("Not enough of information: \"dev\" argument is required."); + exit(-1); + } + + 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; + } + if (newbrd) { + if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) + return -1; + } + } + + if (newname && strcmp(dev, newname)) { + if (do_changename(dev, newname) < 0) + return -1; + dev = newname; + } + if (qlen != -1) { + if (set_qlen(dev, qlen) < 0) + return -1; + } + if (mtu != -1) { + if (set_mtu(dev, mtu) < 0) + return -1; + } + if (newaddr || newbrd) { + if (newbrd) { + if (set_address(&ifr1, 1) < 0) + return -1; + } + if (newaddr) { + if (set_address(&ifr0, 0) < 0) + return -1; + } + } + if (mask) + return do_chflags(dev, flags, mask); + return 0; +} + +static int ipaddr_list_link(int argc, char **argv) +{ + preferred_family = AF_PACKET; + do_link = 1; + return ipaddr_list_or_flush(argc, argv, 0); +} + +int do_iplink(int argc, 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.", *argv); + exit(-1); +} diff --git a/busybox/networking/libiproute/iproute.c b/busybox/networking/libiproute/iproute.c new file mode 100644 index 0000000..9c57140 --- /dev/null +++ b/busybox/networking/libiproute/iproute.c @@ -0,0 +1,852 @@ +/* + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * 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 "rt_names.h" +#include "utils.h" + +#include "libbb.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +static struct +{ + int tb; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; + int protocol, protocolmask; + int scope, scopemask; + int type, typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int realm, realmmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + inet_prefix dst; + 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", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + 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 (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_INET) + host_len = 32; + + if (r->rtm_family == AF_INET6) { + if (filter.tb) { + if (filter.tb < 0) { + if (!(r->rtm_flags&RTM_F_CLONED)) { + return 0; + } + } else { + if (r->rtm_flags&RTM_F_CLONED) { + return 0; + } + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) { + return 0; + } + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) { + return 0; + } + } else { + return 0; + } + } + } + } else { + if (filter.tb > 0 && filter.tb != r->rtm_table) { + return 0; + } + } + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) { + return 0; + } + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) { + return 0; + } + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) { + return 0; + } + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) { + return 0; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if (filter.mdst.family && filter.mdst.bitlen >= 0 && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if (filter.msrc.family && filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + return 0; + } + + if (n->nlmsg_type == RTM_DELROUTE) { + fprintf(fp, "Deleted "); + } + if (r->rtm_type != RTN_UNICAST && !filter.type) { + fprintf(fp, "%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 + ); + } else { + fprintf(fp, "%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); + } else { + fprintf(fp, "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 + ); + } else { + fprintf(fp, "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); + } + 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))); + } + if (tb[RTA_OIF] && filter.oifmask != -1) { + fprintf(fp, "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))); + } + if (tb[RTA_PRIORITY]) { + fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); + } + if (r->rtm_family == AF_INET6) { + struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) { + 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_); + } + if (ci->rta_expires) { + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + } + if (ci->rta_error != 0) { + fprintf(fp, " error %d", ci->rta_error); + } + } else if (ci) { + if (ci->rta_error != 0) + fprintf(fp, " 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]))); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + 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; + + 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_type = cmd; + req.r.rtm_family = preferred_family; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_scope = RT_SCOPE_NOWHERE; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + 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_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + gw_ok = 1; + 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) { + unsigned mtu; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_MTU); + NEXT_ARG(); + } + if (get_unsigned(&mtu, *argv, 0)) { + invarg("\"mtu\" value is invalid\n", *argv); + } + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); + } else if (matches(*argv, "protocol") == 0) { + int prot; + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.r.rtm_protocol = prot; + proto_ok =1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + d = *argv; + } else { + int type; + inet_prefix dst; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if ((**argv < '0' || **argv > '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (dst_ok) { + duparg2("to", *argv); + } + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = dst.family; + } + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) { + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) { + exit(1); + } + + if (d) { + int idx; + + ll_init_map(&rth); + + if (d) { + if ((idx = ll_name_to_index(d)) == 0) { + bb_error_msg("Cannot find device \"%s\"", d); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) { + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + } + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + 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 0; +} + +static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + memset(&req, 0, sizeof(req)); + 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; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.rtm.rtm_family = family; + req.rtm.rtm_flags |= RTM_F_CLONED; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +static int 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"; + + if (flush_fd < 0) { + fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); + return -1; + } + + len = strlen (buffer); + + if ((write (flush_fd, (void *)buffer, len)) < len) { + fprintf (stderr, "Cannot flush routing cache\n"); + return -1; + } + close(flush_fd); + return 0; +} + +static void iproute_reset_filter(void) +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; +} + +static int iproute_list_or_flush(int argc, char **argv, int flush) +{ + int do_ipv6 = preferred_family; + struct rtnl_handle rth; + char *id = NULL; + char *od = NULL; + + iproute_reset_filter(); + filter.tb = RT_TABLE_MAIN; + + if (flush && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "protocol") == 0) { + int prot = 0; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) { + invarg("invalid \"protocol\"\n", *argv); + } + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, do_ipv6); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, do_ipv6); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (do_ipv6 == AF_UNSPEC && filter.tb) { + do_ipv6 = AF_INET; + } + + if (rtnl_open(&rth, 0) < 0) { + exit(1); + } + + 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; + } + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + if ((idx = ll_name_to_index(od)) == 0) { + bb_error_msg("Cannot find device \"%s\"", od); + } + filter.oif = idx; + filter.oifmask = -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + if (filter.tb == -1) { + if (do_ipv6 != AF_INET6) + iproute_flush_cache(); + if (do_ipv6 == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + return -1; + } + 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); + return 0; + } + round++; + if (flush_update() < 0) + exit(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"); + } + + exit(0); +} + + +static int iproute_get(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + 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 }; + + 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; + 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)) { + case 0: /* from */ + { + inet_prefix addr; + NEXT_ARG(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = addr.family; + } + if (addr.bytelen) { + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + } + req.r.rtm_src_len = addr.bitlen; + break; + } + case 1: /* iif */ + NEXT_ARG(); + idev = *argv; + break; + case 2: /* oif */ + case 3: /* dev */ + NEXT_ARG(); + odev = *argv; + break; + case 4: /* notify */ + req.r.rtm_flags |= RTM_F_NOTIFY; + break; + case 5: /* connected */ + connected = 1; + break; + case 6: /* to */ + NEXT_ARG(); + default: + { + inet_prefix addr; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = addr.family; + } + if (addr.bytelen) { + addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); + } + req.r.rtm_dst_len = addr.bitlen; + } + argc--; argv++; + } + } + + if (req.r.rtm_dst_len == 0) { + bb_error_msg_and_die("need at least destination address"); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (idev || odev) { + int idx; + + if (idev) { + if ((idx = ll_name_to_index(idev)) == 0) { + bb_error_msg("Cannot find device \"%s\"", idev); + return -1; + } + 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; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = AF_INET; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { + exit(2); + } + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(&req.n); + 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 :-)"); + } + + if (req.n.nlmsg_type != RTM_NEWROUTE) { + bb_error_msg("Not a route?"); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + bb_error_msg("Wrong len %d", len); + return -1; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + 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; + } + if (!odev && tb[RTA_OIF]) { + tb[RTA_OIF]->rta_type = 0; + } + if (tb[RTA_GATEWAY]) { + tb[RTA_GATEWAY]->rta_type = 0; + } + if (!idev && tb[RTA_IIF]) { + tb[RTA_IIF]->rta_type = 0; + } + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { + exit(2); + } + } + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + bb_error_msg_and_die("An error :-)"); + } + + exit(0); +} + +int do_iproute(int argc, char **argv) +{ + const char *ip_route_commands[] = { "add", "append", "change", "chg", + "delete", "del", "get", "list", "show", "prepend", "replace", "test", "flush", 0 }; + unsigned short command_num = 7; + unsigned int flags = 0; + int cmd = RTM_NEWROUTE; + + if (*argv) { + command_num = compare_string_array(ip_route_commands, *argv); + } + switch(command_num) { + case 0: /* add*/ + flags = NLM_F_CREATE|NLM_F_EXCL; + break; + case 1: /* append */ + flags = NLM_F_CREATE|NLM_F_APPEND; + break; + case 2: /* change */ + case 3: /* chg */ + flags = NLM_F_REPLACE; + break; + case 4: /* delete */ + case 5: /* del */ + cmd = RTM_DELROUTE; + break; + case 6: /* get */ + return iproute_get(argc-1, argv+1); + case 7: /* list */ + case 8: /* show */ + return iproute_list_or_flush(argc-1, argv+1, 0); + case 9: /* prepend */ + flags = NLM_F_CREATE; + case 10: /* replace */ + flags = NLM_F_CREATE|NLM_F_REPLACE; + case 11: /* test */ + flags = NLM_F_EXCL; + case 12: /* flush */ + return iproute_list_or_flush(argc-1, argv+1, 1); + default: + bb_error_msg_and_die("Unknown command %s", *argv); + } + + return iproute_modify(cmd, flags, argc-1, argv+1); +} diff --git a/busybox/networking/libiproute/iptunnel.c b/busybox/networking/libiproute/iptunnel.c new file mode 100644 index 0000000..f8713e0 --- /dev/null +++ b/busybox/networking/libiproute/iptunnel.c @@ -0,0 +1,548 @@ +/* + * 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit + * 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> +#define __constant_htons htons +#include <linux/if_tunnel.h> + +#include "rt_names.h" +#include "utils.h" + +#include "libbb.h" + +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; + } + close(fd); + return ifr.ifr_ifindex; +} + +static int do_ioctl_get_iftype(char *dev) +{ + struct ifreq ifr; + int fd; + + strcpy(ifr.ifr_name, dev); + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(fd, SIOCGIFHWADDR, &ifr)) { + bb_perror_msg("ioctl"); + return -1; + } + close(fd); + return ifr.ifr_addr.sa_family; +} + + +static char *do_ioctl_get_ifname(int idx) +{ + static struct ifreq ifr; + int fd; + + ifr.ifr_ifindex = idx; + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(fd, SIOCGIFNAME, &ifr)) { + bb_perror_msg("ioctl"); + return NULL; + } + close(fd); + return ifr.ifr_name; +} + + + +static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + strcpy(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"); + } + close(fd); + return err; +} + +static int do_add_ioctl(int cmd, 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); + } else { + strcpy(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"); + } + close(fd); + return err; +} + +static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (p->name[0]) { + strcpy(ifr.ifr_name, p->name); + } else { + strcpy(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"); + } + close(fd); + return err; +} + +static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) +{ + int count = 0; + char medium[IFNAMSIZ]; + memset(p, 0, sizeof(*p)); + memset(&medium, 0, sizeof(medium)); + + p->iph.version = 4; + p->iph.ihl = 5; +#ifndef IP_DF +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ +#endif + p->iph.frag_off = htons(IP_DF); + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipip") == 0 || + strcmp(*argv, "ip/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { + bb_error_msg("You managed to ask for more than one tunnel mode."); + exit(-1); + } + p->iph.protocol = IPPROTO_IPIP; + } else if (strcmp(*argv, "gre") == 0 || + strcmp(*argv, "gre/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { + bb_error_msg("You managed to ask for more than one tunnel mode."); + exit(-1); + } + p->iph.protocol = IPPROTO_GRE; + } else if (strcmp(*argv, "sit") == 0 || + strcmp(*argv, "ipv6/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { + bb_error_msg("You managed to ask for more than one tunnel mode."); + exit(-1); + } + p->iph.protocol = IPPROTO_IPV6; + } else { + bb_error_msg("Cannot guess tunnel mode."); + exit(-1); + } + } else if (strcmp(*argv, "key") == 0) { + unsigned uval; + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->o_flags |= GRE_KEY; + 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); + } + p->i_key = p->o_key = htonl(uval); + } + } else if (strcmp(*argv, "ikey") == 0) { + 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); + } + p->i_key = htonl(uval); + } + } else if (strcmp(*argv, "okey") == 0) { + 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); + } + p->o_key = htonl(uval); + } + } else if (strcmp(*argv, "seq") == 0) { + p->i_flags |= GRE_SEQ; + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "iseq") == 0) { + p->i_flags |= GRE_SEQ; + } else if (strcmp(*argv, "oseq") == 0) { + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "csum") == 0) { + p->i_flags |= GRE_CSUM; + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "icsum") == 0) { + p->i_flags |= GRE_CSUM; + } else if (strcmp(*argv, "ocsum") == 0) { + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "nopmtudisc") == 0) { + p->iph.frag_off = 0; + } else if (strcmp(*argv, "pmtudisc") == 0) { + p->iph.frag_off = htons(IP_DF); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.daddr = get_addr32(*argv); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.saddr = get_addr32(*argv); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(medium, *argv, IFNAMSIZ-1); + } else if (strcmp(*argv, "ttl") == 0) { + unsigned uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_unsigned(&uval, *argv, 0)) + invarg("invalid TTL\n", *argv); + if (uval > 255) + invarg("TTL must be <=255\n", *argv); + p->iph.ttl = uval; + } + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + p->iph.tos = uval; + } else + p->iph.tos = 1; + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } + if (p->name[0]) + duparg2("name", *argv); + strncpy(p->name, *argv, IFNAMSIZ); + 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; + *p = old_p; + } + } + count++; + argc--; argv++; + } + + + if (p->iph.protocol == 0) { + if (memcmp(p->name, "gre", 3) == 0) + p->iph.protocol = IPPROTO_GRE; + else if (memcmp(p->name, "ipip", 4) == 0) + p->iph.protocol = IPPROTO_IPIP; + else if (memcmp(p->name, "sit", 3) == 0) + p->iph.protocol = IPPROTO_IPV6; + } + + 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; + } + } + + 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))) { + p->i_key = p->iph.daddr; + p->i_flags |= GRE_KEY; + } + if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->o_key = p->iph.daddr; + 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; + } + return 0; +} + + +static int do_add(int cmd, int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, cmd, &p) < 0) + return -1; + + if (p.iph.ttl && p.iph.frag_off == 0) { + bb_error_msg("ttl != 0 and noptmudisc are incompatible"); + return -1; + } + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_add_ioctl(cmd, "tunl0", &p); + case IPPROTO_GRE: + 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; + } + return -1; +} + +int do_del(int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_del_ioctl("tunl0", &p); + case IPPROTO_GRE: + return do_del_ioctl("gre0", &p); + case IPPROTO_IPV6: + return do_del_ioctl("sit0", &p); + default: + return do_del_ioctl(p.name, &p); + } + return -1; +} + +void print_tunnel(struct ip_tunnel_parm *p) +{ + char s1[256]; + char s2[256]; + char s3[64]; + char s4[64]; + + format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)); + format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)); + inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); + inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); + + printf("%s: %s/ip remote %s local %s ", + p->name, + p->iph.protocol == IPPROTO_IPIP ? "ip" : + (p->iph.protocol == IPPROTO_GRE ? "gre" : + (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), + p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); + if (p->link) { + char *n = do_ioctl_get_ifname(p->link); + if (n) + printf(" dev %s ", n); + } + if (p->iph.ttl) + printf(" ttl %d ", p->iph.ttl); + else + printf(" ttl inherit "); + if (p->iph.tos) { + SPRINT_BUF(b1); + printf(" tos"); + 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.frag_off&htons(IP_DF))) + printf(" nopmtudisc"); + + 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) + printf(" ikey %s ", s3); + 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_); +} + +static int 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; + int type; + struct ip_tunnel_parm p1; + + char buf[512]; + FILE *fp = fopen("/proc/net/dev", "r"); + if (fp == NULL) { + perror("fopen"); + return -1; + } + + 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 || + (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { + bb_error_msg("Wrong format of /proc/net/dev. Sorry."); + return -1; + } + if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", + &rx_bytes, &rx_packets, &rx_errs, &rx_drops, + &rx_fifo, &rx_frame, &rx_multi, + &tx_bytes, &tx_packets, &tx_errs, &tx_drops, + &tx_fifo, &tx_colls, &tx_carrier) != 14) + continue; + if (p->name[0] && strcmp(p->name, name)) + continue; + type = do_ioctl_get_iftype(name); + if (type == -1) { + bb_error_msg("Failed to get type of [%s]", name); + continue; + } + if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) + continue; + memset(&p1, 0, sizeof(p1)); + if (do_get_ioctl(name, &p1)) + continue; + if ((p->link && p1.link != p->link) || + (p->name[0] && strcmp(p1.name, p->name)) || + (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || + (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || + (p->i_key && p1.i_key != p->i_key)) + continue; + print_tunnel(&p1); + printf("\n"); + } + return 0; +} + +static int do_show(int argc, char **argv) +{ + int err; + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); + break; + case IPPROTO_GRE: + err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); + break; + case IPPROTO_IPV6: + err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); + break; + default: + do_tunnels_list(&p); + return 0; + } + if (err) + return -1; + + print_tunnel(&p); + printf("\n"); + return 0; +} + +int do_iptunnel(int argc, 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.", *argv); + exit(-1); +} diff --git a/busybox/networking/libiproute/libnetlink.c b/busybox/networking/libiproute/libnetlink.c new file mode 100644 index 0000000..5545be8 --- /dev/null +++ b/busybox/networking/libiproute/libnetlink.c @@ -0,0 +1,524 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * 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> + * + */ + +#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" + +void rtnl_close(struct rtnl_handle *rth) +{ + close(rth->fd); +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + int 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; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + 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; + } + 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; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + 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)); +} + +int 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)); +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr; + struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + iov, 2, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + 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) +{ + char buf[8192]; + struct sockaddr_nl nladdr; + struct iovec iov = { buf, sizeof(buf) }; + + while (1) { + int status; + struct nlmsghdr *h; + + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("OVERRUN"); + continue; + } + 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); + } + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + 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; + } + } + goto skip_it; + } + + if (h->nlmsg_type == NLMSG_DONE) { + return 0; + } + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + bb_error_msg("ERROR truncated"); + } else { + errno = -l_err->error; + bb_perror_msg("RTNETLINK answers"); + } + return -1; + } + err = filter(&nladdr, h, arg1); + if (err < 0) { + return err; + } + +skip_it: + h = NLMSG_NEXT(h, status); + } + 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_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) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { (void*)n, n->nlmsg_len }; + 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 = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + if (answer == NULL) { + n->nlmsg_flags |= NLM_F_ACK; + } + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + bb_perror_msg("Cannot talk to rtnetlink"); + return -1; + } + + 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; + } + 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 l_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); + } + + 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; + } + } + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { + bb_error_msg("ERROR truncated"); + } else { + errno = -err->error; + if (errno == 0) { + if (answer) { + memcpy(answer, h, h->nlmsg_len); + } + return 0; + } + bb_perror_msg("RTNETLINK answers"); + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + 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"); + 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; + } + 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; + } +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + if (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); + 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 len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (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, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + 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) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) { + tb[rta->rta_type] = rta; + } + rta = RTA_NEXT(rta,len); + } + if (len) { + bb_error_msg("!!!Deficit %d, rta_len=%d", len, rta->rta_len); + } + return 0; +} diff --git a/busybox/networking/libiproute/libnetlink.h b/busybox/networking/libiproute/libnetlink.h new file mode 100644 index 0000000..45d3ad2 --- /dev/null +++ b/busybox/networking/libiproute/libnetlink.h @@ -0,0 +1,46 @@ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 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 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); + + +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 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); + +#endif /* __LIBNETLINK_H__ */ + diff --git a/busybox/networking/libiproute/linux/pkt_sched.h b/busybox/networking/libiproute/linux/pkt_sched.h new file mode 100644 index 0000000..70cbabc --- /dev/null +++ b/busybox/networking/libiproute/linux/pkt_sched.h @@ -0,0 +1,413 @@ +#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/busybox/networking/libiproute/ll_addr.c b/busybox/networking/libiproute/ll_addr.c new file mode 100644 index 0000000..ada685f --- /dev/null +++ b/busybox/networking/libiproute/ll_addr.c @@ -0,0 +1,81 @@ +/* + * ll_addr.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. + * + * 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" + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen) +{ + int i; + int l; + + if (alen == 4 && + (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) { + return inet_ntop(AF_INET, addr, buf, blen); + } + l = 0; + for (i=0; i<alen; i++) { + if (i==0) { + snprintf(buf+l, blen, "%02x", addr[i]); + blen -= 2; + l += 2; + } else { + snprintf(buf+l, blen, ":%02x", addr[i]); + blen -= 3; + l += 3; + } + } + return buf; +} + +int ll_addr_a2n(unsigned char *lladdr, int len, char *arg) +{ + if (strchr(arg, '.')) { + inet_prefix pfx; + if (get_addr_1(&pfx, arg, AF_INET)) { + bb_error_msg("\"%s\" is invalid lladdr.", arg); + return -1; + } + if (len < 4) { + return -1; + } + 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; + } + return i+1; + } +} diff --git a/busybox/networking/libiproute/ll_map.c b/busybox/networking/libiproute/ll_map.c new file mode 100644 index 0000000..b7a8284 --- /dev/null +++ b/busybox/networking/libiproute/ll_map.c @@ -0,0 +1,164 @@ +/* + * ll_map.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <netinet/in.h> + +#include "libnetlink.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) +{ + int h; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct idxmap *im, **imp; + struct rtattr *tb[IFLA_MAX+1]; + + if (n->nlmsg_type != RTM_NEWLINK) + return 0; + + 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; + + 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; + } + + 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)) + alen = sizeof(im->addr); + memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen); + } else { + im->alen = 0; + memset(im->addr, 0, sizeof(im->addr)); + } + strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME])); + return 0; +} + +const char *ll_idx_n2a(int idx, char *buf) +{ + struct idxmap *im; + + if (idx == 0) + return "*"; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->name; + snprintf(buf, 16, "if%d", idx); + return buf; +} + + +const char *ll_index_to_name(int idx) +{ + static char nbuf[16]; + + return ll_idx_n2a(idx, nbuf); +} + +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; + return -1; +} + +unsigned ll_index_to_flags(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return 0; + + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->flags; + return 0; +} + +int ll_name_to_index(char *name) +{ + 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++) { + for (im = idxmap[i]; im; im = im->next) { + if (strcmp(im->name, name) == 0) { + icache = im->index; + strcpy(ncache, name); + return im->index; + } + } + } + return 0; +} + +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); + } + return 0; +} diff --git a/busybox/networking/libiproute/ll_map.h b/busybox/networking/libiproute/ll_map.h new file mode 100644 index 0000000..739f157 --- /dev/null +++ b/busybox/networking/libiproute/ll_map.h @@ -0,0 +1,12 @@ +#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__ */ diff --git a/busybox/networking/libiproute/ll_proto.c b/busybox/networking/libiproute/ll_proto.c new file mode 100644 index 0000000..9b5260b --- /dev/null +++ b/busybox/networking/libiproute/ll_proto.c @@ -0,0 +1,120 @@ +/* + * ll_proto.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <arpa/inet.h> +#include <string.h> +#include "utils.h" + +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include <net/ethernet.h> +#else +#include <linux/if_ether.h> +#endif + +#define __PF(f,n) { ETH_P_##f, #n }, +static struct { + int id; + char *name; +} llproto_names[] = { +__PF(LOOP,loop) +__PF(PUP,pup) +#ifdef ETH_P_PUPAT +__PF(PUPAT,pupat) +#endif +__PF(IP,ip) +__PF(X25,x25) +__PF(ARP,arp) +__PF(BPQ,bpq) +#ifdef ETH_P_IEEEPUP +__PF(IEEEPUP,ieeepup) +#endif +#ifdef ETH_P_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) +#ifdef ETH_P_PPP_DISC +__PF(PPP_DISC,ppp_disc) +#endif +#ifdef ETH_P_PPP_SES +__PF(PPP_SES,ppp_ses) +#endif +#ifdef ETH_P_ATMMPOA +__PF(ATMMPOA,atmmpoa) +#endif +#ifdef ETH_P_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) +#ifdef ETH_P_ECONET +__PF(ECONET,econet) +#endif + +{ 0x8100, "802.1Q" }, +{ ETH_P_IP, "ipv4" }, +}; +#undef __PF + + +char * ll_proto_n2a(unsigned short id, char *buf, int len) +{ + int i; + + id = ntohs(id); + + for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (llproto_names[i].id == id) + return llproto_names[i].name; + } + 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; + } + } + if (get_u16(id, buf, 0)) + return -1; + *id = htons(*id); + return 0; +} diff --git a/busybox/networking/libiproute/ll_types.c b/busybox/networking/libiproute/ll_types.c new file mode 100644 index 0000000..f39f777 --- /dev/null +++ b/busybox/networking/libiproute/ll_types.c @@ -0,0 +1,115 @@ +/* + * ll_types.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. + * + * 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) +{ +#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) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802,ieee802) +#else +__PF(IEEE802,tr) +#endif +__PF(ARCNET,arcnet) +__PF(APPLETLK,atalk) +__PF(DLCI,dlci) +#ifdef ARPHRD_ATM +__PF(ATM,atm) +#endif +__PF(METRICOM,metricom) +#ifdef ARPHRD_IEEE1394 +__PF(IEEE1394,ieee1394) +#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) +#ifdef ARPHRD_HWX25 +__PF(HWX25,hwx25) +#endif +__PF(PPP,ppp) +__PF(HDLC,hdlc) +__PF(LAPB,lapb) +#ifdef ARPHRD_DDCMP +__PF(DDCMP,ddcmp) +__PF(RAWHDLC,rawhdlc) +#endif + +__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) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802_TR,tr) +#endif +#ifdef ARPHRD_IEEE80211 +__PF(IEEE80211,ieee802.11) +#endif +#ifdef ARPHRD_VOID +__PF(VOID,void) +#endif +}; +#undef __PF + + 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; + } + snprintf(buf, len, "[%d]", type); + return buf; +} diff --git a/busybox/networking/libiproute/rt_names.c b/busybox/networking/libiproute/rt_names.c new file mode 100644 index 0000000..d503645 --- /dev/null +++ b/busybox/networking/libiproute/rt_names.c @@ -0,0 +1,385 @@ +/* + * rt_names.c rtnetlink names DB. + * + * 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> + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <stdint.h> + +static void rtnl_tab_initialize(char *file, 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; + } + + if (id<0 || id>size) + continue; + + tab[id] = strdup(namebuf); + } + fclose(fp); +} + + +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 void rtnl_rtprot_initialize(void) +{ + rtnl_rtprot_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_protos", + rtnl_rtprot_tab, 256); +} + +char * rtnl_rtprot_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtprot_tab[id]) { + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + } + if (rtnl_rtprot_tab[id]) + return rtnl_rtprot_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtprot_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtprot_tab[i] && + strcmp(rtnl_rtprot_tab[i], arg) == 0) { + cache = rtnl_rtprot_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtscope_tab[256] = { + "global", +}; + +static int rtnl_rtscope_init; + +static void rtnl_rtscope_initialize(void) +{ + rtnl_rtscope_init = 1; + rtnl_rtscope_tab[255] = "nowhere"; + rtnl_rtscope_tab[254] = "host"; + rtnl_rtscope_tab[253] = "link"; + rtnl_rtscope_tab[200] = "site"; + rtnl_tab_initialize("/etc/iproute2/rt_scopes", + rtnl_rtscope_tab, 256); +} + +char * rtnl_rtscope_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtscope_tab[id]) { + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + } + if (rtnl_rtscope_tab[id]) + return rtnl_rtscope_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtscope_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtscope_tab[i] && + strcmp(rtnl_rtscope_tab[i], arg) == 0) { + cache = rtnl_rtscope_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtrealm_tab[256] = { + "unknown", +}; + +static int rtnl_rtrealm_init; + +static void rtnl_rtrealm_initialize(void) +{ + rtnl_rtrealm_init = 1; + 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 unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtrealm_tab[i] && + strcmp(rtnl_rtrealm_tab[i], arg) == 0) { + cache = rtnl_rtrealm_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rttable_tab[256] = { + "unspec", +}; + +static int rtnl_rttable_init; + +static void rtnl_rttable_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); +} + +char * rtnl_rttable_n2a(int id, char *buf, int len) +{ + 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); + return buf; +} + +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) { + *id = res; + return 0; + } + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rttable_tab[i] && + strcmp(rtnl_rttable_tab[i], arg) == 0) { + cache = rtnl_rttable_tab[i]; + res = i; + *id = res; + return 0; + } + } + + i = strtoul(arg, &end, 0); + if (!end || end == arg || *end || i > 255) + return -1; + *id = i; + return 0; +} + + +static char * rtnl_rtdsfield_tab[256] = { + "0", +}; + +static int rtnl_rtdsfield_init; + +static void rtnl_rtdsfield_initialize(void) +{ + rtnl_rtdsfield_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_dsfield", + rtnl_rtdsfield_tab, 256); +} + +char * rtnl_dsfield_n2a(int id, char *buf, int len) +{ + 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); + return buf; +} + + +int rtnl_dsfield_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + + 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; + } + } + + res = strtoul(arg, &end, 16); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + diff --git a/busybox/networking/libiproute/rt_names.h b/busybox/networking/libiproute/rt_names.h new file mode 100644 index 0000000..97bc616 --- /dev/null +++ b/busybox/networking/libiproute/rt_names.h @@ -0,0 +1,30 @@ +#ifndef RT_NAMES_H_ +#define RT_NAMES_H_ 1 + +#include <stdint.h> + +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); + +const char *inet_proto_n2a(int proto, char *buf, int len); +int inet_proto_a2n(char *buf); + + +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); + + +#endif diff --git a/busybox/networking/libiproute/rtm_map.c b/busybox/networking/libiproute/rtm_map.c new file mode 100644 index 0000000..5f6a9e6 --- /dev/null +++ b/busybox/networking/libiproute/rtm_map.c @@ -0,0 +1,110 @@ +/* + * rtm_map.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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdlib.h> +#include <string.h> + +#include "rt_names.h" +#include "utils.h" + +char *rtnl_rtntype_n2a(int id, char *buf, int len) +{ + switch (id) { + case RTN_UNSPEC: + return "none"; + case RTN_UNICAST: + return "unicast"; + case RTN_LOCAL: + return "local"; + case RTN_BROADCAST: + return "broadcast"; + case RTN_ANYCAST: + return "anycast"; + case RTN_MULTICAST: + return "multicast"; + case RTN_BLACKHOLE: + return "blackhole"; + case RTN_UNREACHABLE: + return "unreachable"; + case RTN_PROHIBIT: + return "prohibit"; + case RTN_THROW: + return "throw"; + case RTN_NAT: + return "nat"; + case RTN_XRESOLVE: + return "xresolve"; + default: + snprintf(buf, len, "%d", id); + return buf; + } +} + + +int rtnl_rtntype_a2n(int *id, char *arg) +{ + char *end; + unsigned long res; + + if (strcmp(arg, "local") == 0) + res = RTN_LOCAL; + else if (strcmp(arg, "nat") == 0) + res = RTN_NAT; + else if (matches(arg, "broadcast") == 0 || + strcmp(arg, "brd") == 0) + res = RTN_BROADCAST; + else if (matches(arg, "anycast") == 0) + res = RTN_ANYCAST; + else if (matches(arg, "multicast") == 0) + res = RTN_MULTICAST; + else if (matches(arg, "prohibit") == 0) + res = RTN_PROHIBIT; + else if (matches(arg, "unreachable") == 0) + res = RTN_UNREACHABLE; + else if (matches(arg, "blackhole") == 0) + res = RTN_BLACKHOLE; + else if (matches(arg, "xresolve") == 0) + res = RTN_XRESOLVE; + else if (matches(arg, "unicast") == 0) + res = RTN_UNICAST; + else if (strcmp(arg, "throw") == 0) + res = RTN_THROW; + else { + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + } + *id = res; + return 0; +} + +int get_rt_realms(__u32 *realms, char *arg) +{ + __u32 realm = 0; + char *p = strchr(arg, '/'); + + *realms = 0; + if (p) { + *p = 0; + if (rtnl_rtrealm_a2n(realms, arg)) { + *p = '/'; + return -1; + } + *realms <<= 16; + *p = '/'; + arg = p+1; + } + if (*arg && rtnl_rtrealm_a2n(&realm, arg)) + return -1; + *realms |= realm; + return 0; +} diff --git a/busybox/networking/libiproute/rtm_map.h b/busybox/networking/libiproute/rtm_map.h new file mode 100644 index 0000000..70bda7d --- /dev/null +++ b/busybox/networking/libiproute/rtm_map.h @@ -0,0 +1,10 @@ +#ifndef __RTM_MAP_H__ +#define __RTM_MAP_H__ 1 + +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); + + +#endif /* __RTM_MAP_H__ */ diff --git a/busybox/networking/libiproute/utils.c b/busybox/networking/libiproute/utils.c new file mode 100644 index 0000000..fa15486 --- /dev/null +++ b/busybox/networking/libiproute/utils.c @@ -0,0 +1,359 @@ +/* + * 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. + * + * 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" + +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 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; +} + +int get_u16(__u16 * 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 > 0xFFFF) + return -1; + *val = res; + return 0; +} + +int get_u8(__u8 * 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 > 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; +} + +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) { + addr->family = family; + addr->bytelen = (family == AF_INET6 ? 16 : 4); + addr->bitlen = -1; + return 0; + } + + if (strchr(name, ':')) { + addr->family = AF_INET6; + if (family != AF_UNSPEC && family != AF_INET6) + return -1; + if (inet_pton(AF_INET6, name, addr->data) <= 0) + return -1; + addr->bytelen = 16; + addr->bitlen = -1; + return 0; + } + + addr->family = AF_INET; + if (family != AF_UNSPEC && family != AF_INET) + 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) +{ + int err; + unsigned plen; + char *slash; + + memset(dst, 0, sizeof(*dst)); + + if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) { + dst->family = family; + dst->bytelen = 0; + dst->bitlen = 0; + return 0; + } + + slash = strchr(arg, '/'); + if (slash) + *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; + } + if (slash) { + if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) { + err = -1; + goto done; + } + dst->bitlen = plen; + } + } + done: + if (slash) + *slash = '/'; + return err; +} + +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); + } + if (get_addr_1(dst, arg, family)) { + bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg); + } + return 0; +} + +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); + } + if (get_prefix_1(dst, arg, family)) { + bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg); + } + return 0; +} + +__u32 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); + } + return addr.data[0]; +} + +void incomplete_command() +{ + bb_error_msg("Command line is not complete. Try option \"help\""); + exit(-1); +} + +void invarg(char *msg, char *arg) +{ + bb_error_msg("argument \"%s\" is wrong: %s", arg, msg); + exit(-1); +} + +void duparg(char *key, char *arg) +{ + bb_error_msg("duplicate \"%s\": \"%s\" is the second value.", key, arg); + exit(-1); +} + +void duparg2(char *key, char *arg) +{ + bb_error_msg("either \"%s\" is duplicate, or \"%s\" is a garbage.", key, arg); + exit(-1); +} + +int matches(char *cmd, char *pattern) +{ + 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; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + 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) +{ + switch (af) { + case AF_INET: + case AF_INET6: + return inet_ntop(af, addr, buf, buflen); + default: + return "???"; + } +} + + +const char *format_host(int af, int len, void *addr, char *buf, int buflen) +{ +#ifdef RESOLVE_HOSTNAMES + if (resolve_hosts) { + struct hostent *h_ent; + + if (len <= 0) { + switch (af) { + case AF_INET: + len = 4; + break; + case AF_INET6: + len = 16; + break; + default:; + } + } + if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) { + snprintf(buf, buflen - 1, "%s", h_ent->h_name); + return buf; + } + } +#endif + return rt_addr_n2a(af, len, addr, buf, buflen); +} diff --git a/busybox/networking/libiproute/utils.h b/busybox/networking/libiproute/utils.h new file mode 100644 index 0000000..e79e177 --- /dev/null +++ b/busybox/networking/libiproute/utils.h @@ -0,0 +1,101 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ 1 + +#include <asm/types.h> +#include <resolv.h> + +#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_; + +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif + +#define SPRINT_BSIZE 64 +#define SPRINT_BUF(x) char x[SPRINT_BSIZE] + +extern void incomplete_command(void) __attribute__((noreturn)); + +#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) + +typedef struct +{ + __u8 family; + __u8 bytelen; + __s16 bitlen; + __u32 data[4]; +} inet_prefix; + +#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]; +}; + +#define IPX_NODE_LEN 6 + +struct ipx_addr { + uint32_t ipx_net; + uint8_t ipx_node[IPX_NODE_LEN]; +}; + +extern __u32 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_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 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); + +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); + +const char *dnet_ntop(int af, const void *addr, char *str, size_t len); +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; +} + +#endif /* __UTILS_H__ */ diff --git a/busybox/networking/nameif.c b/busybox/networking/nameif.c new file mode 100644 index 0000000..10f13b4 --- /dev/null +++ b/busybox/networking/nameif.c @@ -0,0 +1,222 @@ +/* + * nameif.c - Naming Interfaces based on MAC address for busybox. + * + * Written 2000 by Andi Kleen. + * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua> + * Glenn McGrath <bug1@iinet.net.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 + * + */ + + +#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 <net/if.h> +#include <netinet/ether.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 +#endif + +/* take from linux/sockios.h */ +#define SIOCSIFNAME 0x8923 /* set interface name */ + +/* 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; + char *ifname; + struct ether_addr *mac; +} mactable_t; + +static unsigned char use_syslog; + +static void serror(const char *s, ...) __attribute__ ((noreturn)); + +static void serror(const char *s, ...) +{ + va_list ap; + + va_start(ap, s); + + 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); + } + + va_end(ap); + + exit(EXIT_FAILURE); +} + +/* Check ascii str_macaddr, convert and copy to *mac */ +struct ether_addr *cc_macaddr(char *str_macaddr) +{ + 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; +} + +int nameif_main(int argc, char **argv) +{ + mactable_t *clist = NULL; + FILE *ifh; + 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(); + } + } + + if ((argc - optind) & 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; + } + } 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); + } + + if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1) + serror("socket: %m"); + + while (clist) { + struct ifreq 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)) + 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) + 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); + + /* Remove list entry of renamed interface */ + 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 + } + + return 0; +} diff --git a/busybox/networking/nc.c b/busybox/networking/nc.c new file mode 100644 index 0000000..3099763 --- /dev/null +++ b/busybox/networking/nc.c @@ -0,0 +1,177 @@ +/* vi: set sw=4 ts=4: */ +/* nc: mini-netcat - built from the ground up for LRP + Copyright (C) 1998 Charles P. Wright + + 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() + + 19980918 Busy Boxed! Dave Cinege + 19990512 Uses Select. Charles P. Wright + 19990513 Fixes stdin stupidity and uses buffers. Charles P. Wright + + 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 <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 + +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; + + fd_set readfds, testfds; + + while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) { + switch (opt) { + case 'l': + do_listen++; + break; + case 'p': + lport = bb_lookup_port(optarg, "tcp", 0); + break; + case 'i': + delay = atoi(optarg); + break; +#ifdef GAPING_SECURITY_HOLE + case 'e': + pr00gie = optarg; + break; +#endif + default: + bb_show_usage(); + } + } + +#ifdef GAPING_SECURITY_HOLE + if (pr00gie) { + /* won't need stdin */ + close (STDIN_FILENO); + } +#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 = lport; + + if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + bb_perror_msg_and_die("bind"); + } + + 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 = bb_lookup_port(argv[optind+1], "tcp", 0); + + if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + bb_perror_msg_and_die("connect"); + } + +#ifdef GAPING_SECURITY_HOLE + /* -e given? */ + if (pr00gie) { + dup2(sfd, 0); + close(sfd); + dup2 (0, 1); + dup2 (0, 2); + execl (pr00gie, pr00gie, NULL); + /* Don't print stuff or it will go over the wire.... */ + _exit(-1); + } +#endif /* GAPING_SECURITY_HOLE */ + + + FD_ZERO(&readfds); + FD_SET(sfd, &readfds); + FD_SET(STDIN_FILENO, &readfds); + + while (1) { + int fd; + int ofd; + int nread; + + testfds = readfds; + + if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0) + bb_perror_msg_and_die("select"); + + 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); + 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); + } + } + } + } +} diff --git a/busybox/networking/netstat.c b/busybox/networking/netstat.c new file mode 100644 index 0000000..bc1ed05 --- /dev/null +++ b/busybox/networking/netstat.c @@ -0,0 +1,661 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini netstat implementation(s) for busybox + * based in part on the netstat implementation from net-tools. + * + * 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> + */ + +#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 "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 + +static int flags = NETSTAT_CONNECTED | + NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX; + +#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH) +#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s) +#define PROGNAME_WIDTH2(s) #s + +#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 */ +}; + +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 */ +} 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 */ + +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; +} + +static char *get_sname(int port, const char *proto, int num) +{ + char *str=itoa(ntohs(port)); + if (num) { + } else { + struct servent *se=getservbyport(port,proto); + if (se) + str=se->s_name; + } + if (!port) { + str="*"; + } + return str; +} + +static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric) +{ + char *port_name; + +#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); +} + +static void tcp_do_one(int lnr, const char *line) +{ + 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; +#else + struct sockaddr_in localaddr, remaddr; +#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); + + 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; +#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; + } + + if (num < 10) { + bb_error_msg("warning, got bogus tcp line."); + return; + } + 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); + + } +} + +static void udp_do_one(int lnr, const char *line) +{ + 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; + + 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); + + 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; + } + + if (num < 10) { + bb_error_msg("warning, got bogus udp line."); + return; + } + switch (state) { + case TCP_ESTABLISHED: + state_str = "ESTABLISHED"; + break; + + case TCP_CLOSE: + state_str = ""; + break; + + 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)) +#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, + "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); + + } +} + +static void raw_do_one(int lnr, const char *line) +{ + 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; + + 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); + + 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; +#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; + } + + if (num < 10) { + bb_error_msg("warning, got bogus raw line."); + return; + } + 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)) +#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); + + } +} + +#define HAS_INODE 1 + +static void unix_do_one(int nr, const 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); + if (num < 6) { + bb_error_msg("warning, got bogus unix line."); + return; + } + if (!(has & HAS_INODE)) + snprintf(path,sizeof(path),"%d",inode); + + if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) { + if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) { + if (!(flags&NETSTAT_LISTENING)) + return; + } else { + if (!(flags&NETSTAT_CONNECTED)) + return; + } + } + + switch (proto) { + case 0: + ss_proto = "unix"; + break; + + default: + ss_proto = "??"; + } + + switch (type) { + 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"; + } + + switch (state) { + case SS_FREE: + ss_state = "FREE"; + break; + + case SS_UNCONNECTED: + /* + * Unconnected sockets may be listening + * for something. + */ + if (unix_flags & SO_ACCEPTCON) { + ss_state = "LISTENING"; + } else { + 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"; + } + + strcpy(ss_flags, "[ "); + if (unix_flags & SO_ACCEPTCON) + strcat(ss_flags, "ACC "); + if (unix_flags & SO_WAITDATA) + 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); +} + +#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" + +static void do_info(const char *file, const char *name, void (*proc)(int, const char *)) +{ + char buffer[8192]; + int lnr = 0; + FILE *procinfo; + + procinfo = fopen(file, "r"); + 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); + } +} + +/* + * Our main function. + */ + +int netstat_main(int argc, char **argv) +{ + int opt; + int new_flags=0; + int showroute = 0, extended = 0; +#ifdef CONFIG_FEATURE_IPV6 + int inet=1; + int inet6=1; +#else +# define inet 1 +# define inet6 0 +#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." ); +#endif + } + + if (new_flags) { + flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX); + flags |= new_flags; + } + if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) { + printf("Active Internet connections "); /* xxx */ + + 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"); + } + 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); +#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); +#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); +#endif + if (flags&NETSTAT_UNIX) { + printf("Active UNIX domain sockets "); + 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); + } + return 0; +} diff --git a/busybox/networking/nslookup.c b/busybox/networking/nslookup.c new file mode 100644 index 0000000..936fa33 --- /dev/null +++ b/busybox/networking/nslookup.c @@ -0,0 +1,206 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini nslookup implementation for busybox + * + * 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 + * 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 + * + */ + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <stdint.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" + +/* + | I'm only implementing non-interactive mode; + | I totally forgot nslookup even had an interactive mode. + */ + +/* only works for IPv4 */ +static int addr_fprint(char *addr) +{ + uint8_t split[4]; + uint32_t ip; + uint32_t *x = (uint32_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 + */ +static int addr_list_fprint(char **h_addr_list) +{ + 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(", "); + } + } + + } + 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"); + } + return host; +} + +/* changes a c-string matching the perl regex \d+\.\d+\.\d+\.\d+ + * into a uint32_t + */ +static uint32_t str_to_addr(const char *addr) +{ + uint32_t split[4]; + uint32_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 */ +} + +/* lookup the default nameserver and display it */ +static inline 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"); +} + +/* 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) +{ + struct in_addr server_in_addr; + + if(inet_aton(server,&server_in_addr)) + { + _res.nscount = 1; + _res.nsaddr_list[0].sin_addr = server_in_addr; + } +} + +/* 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; + } + return 1; +} + +/* ________________________________________________________________________ */ +int nslookup_main(int argc, char **argv) +{ + struct hostent *host; + + /* + * initialize DNS structure _res used in printing the default + * name server and in the explicit name server option feature. + */ + + res_init(); + + /* + * 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) + 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: "); + if (host) { + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +/* $Id: nslookup.c,v 1.33 2004/10/13 07:25:01 andersen Exp $ */ diff --git a/busybox/networking/ping.c b/busybox/networking/ping.c new file mode 100644 index 0000000..50f3930 --- /dev/null +++ b/busybox/networking/ping.c @@ -0,0 +1,472 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen 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. + */ + +#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 <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); + +/* common routines */ +static int in_cksum(unsigned short *buf, int sz) +{ + int nleft = sz; + int sum = 0; + unsigned short *w = buf; + unsigned short ans = 0; + + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + if (nleft == 1) { + *(unsigned char *) (&ans) = *(unsigned char *) w; + sum += ans; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + ans = ~sum; + return (ans); +} + +/* simple version */ +#ifndef CONFIG_FEATURE_FANCY_PING +static char *hostname = NULL; +static void noresp(int ign) +{ + printf("No response from %s\n", hostname); + exit(EXIT_FAILURE); +} + +static void ping(const char *host) +{ + 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; + + 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"); + + 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); + + if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("recvfrom"); + continue; + } + if (c >= 76) { /* ip + icmp */ + struct iphdr *iphdr = (struct iphdr *) packet; + + pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */ + if (pkt->icmp_type == ICMP_ECHOREPLY) + break; + } + } + printf("%s is alive!\n", hostname); + return; +} + +extern int ping_main(int argc, char **argv) +{ + argc--; + argv++; + if (argc < 1) + bb_show_usage(); + ping(*argv); + return EXIT_SUCCESS; +} + +#else /* ! CONFIG_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]; + +struct hostent *hostent; + +static void sendping(int); +static void pingstats(int); +static void unpack(char *, int, struct sockaddr_in *); + +/**************************************************************************/ + +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 icmp *pkt; + int i; + char packet[datalen + 8]; + + pkt = (struct icmp *) packet; + + pkt->icmp_type = ICMP_ECHO; + pkt->icmp_code = 0; + pkt->icmp_cksum = 0; + pkt->icmp_seq = ntransmitted++; + 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)); + + i = sendto(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); + + 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 *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"; + } +} + +static void unpack(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); + + /* check IP header */ + iphdr = (struct iphdr *) buf; + hlen = iphdr->ihl << 2; + /* discard if too short */ + if (sz < (datalen + ICMP_MINLEN)) + return; + + sz -= hlen; + icmppkt = (struct icmp *) (buf + hlen); + + if (icmppkt->icmp_id != myid) + 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; + + if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(icmppkt->icmp_seq % MAX_DUP_CHK); + dupflag = 0; + } + + 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)); +} + +static void ping(const char *host) +{ + char packet[datalen + MAXIPLEN + MAXICMPLEN]; + int sockopt; + + pingsock = create_icmp_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + 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."); + + memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr)); + + /* 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)); + + printf("PING %s (%s): %d data bytes\n", + hostent->h_name, + inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr), + datalen); + + signal(SIGINT, pingstats); + + /* start the ping's going ... */ + sendping(0); + + /* listen for replies */ + 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"); + continue; + } + unpack(packet, c, &from); + if (pingcount > 0 && nreceived >= pingcount) + break; + } + pingstats(0); +} + +extern int ping_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 '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(); + } + argc--; + argv++; + } + if (argc < 1) + bb_show_usage(); + + myid = getpid() & 0xFFFF; + ping(*argv); + return EXIT_SUCCESS; +} +#endif /* ! CONFIG_FEATURE_FANCY_PING */ + +/* + * 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/busybox/networking/ping6.c b/busybox/networking/ping6.c new file mode 100644 index 0000000..72867f3 --- /dev/null +++ b/busybox/networking/ping6.c @@ -0,0 +1,515 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: ping6.c,v 1.6 2004/03/15 08:28:48 andersen 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/busybox/networking/route.c b/busybox/networking/route.c new file mode 100644 index 0000000..5c092f4 --- /dev/null +++ b/busybox/networking/route.c @@ -0,0 +1,714 @@ +/* route + * + * Similar to the standard Unix route, but with only the necessary + * parts for AF_INET and AF_INET6 + * + * Bjorn Wesen, Axis Communications AB + * + * Author of the original route: + * 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. + * + * $Id: route.c,v 1.26 2004/03/19 23:27:08 mjn3 Exp $ + * + * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru> + * adjustments by Larry Doolittle <LRDoolittle@lbl.gov> + * + * IPV6 support added by Bart Visscher <magick@linux-fan.com> + */ + +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <net/route.h> +#include <net/if.h> +#include "busybox.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 +#define RTF_WINDOW 0x0080 /* per route window clamping */ +#define RTF_IRTT 0x0100 /* Initial round trip time */ +#define RTF_REJECT 0x0200 /* Reject route */ +#endif + +#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ +#define HAVE_NEW_ADDRT 1 +#endif + +#if HAVE_NEW_ADDRT +#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr) +#define full_mask(x) (x) +#else +#define mask_in_addr(x) ((x).rt_genmask) +#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[] = + "\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[] = + /* 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. */ + +static void INET_setroute(int action, char **args) +{ + struct rtentry rt; + const char *netmask; + int skfd, isnet, xflag; + + assert((action == RTACTION_ADD) || (action == RTACTION_DEL)); + + /* Grab the -net or -host options. Remember they were transformed. */ + xflag = kw_lookup(tbl_hash_net_host, &args); + + /* If we did grab -net or -host, make sure we still have an arg left. */ + if (*args == NULL) { + bb_show_usage(); + } + + /* Clean out the RTREQ structure. */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + + { + const char *target = *args++; + + /* 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 (xflag) { /* Reinit isnet if -net or -host was specified. */ + isnet = (xflag & NET_FLAG); + } + + /* Fill in the other fields. */ + rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST)); + + netmask = bb_INET_default; + + while (*args) { + 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; + } + +#if HAVE_NEW_ADDRT + if (k == KW_IPVx_METRIC) { + rt.rt_metric = bb_xgetularg10(args_m1) + 1; + continue; + } +#endif + + if (k == KW_IPVx_NETMASK) { + struct sockaddr mask; + + if (mask_in_addr(rt)) { + bb_show_usage(); + } + + 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); + continue; + } + + if (k == KW_IPVx_GATEWAY) { + if (rt.rt_flags & RTF_GATEWAY) { + bb_show_usage(); + } + + isnet = INET_resolve(args_m1, + (struct sockaddr_in *) &rt.rt_gateway, 1); + rt.rt_flags |= RTF_GATEWAY; + + if (isnet) { + if (isnet < 0) { + bb_error_msg_and_die("resolving %s", args_m1); + } + bb_error_msg_and_die("gateway %s is a NETWORK", args_m1); + } + continue; + } + + if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */ + rt.rt_flags |= RTF_MSS; + rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768); + continue; + } + + if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */ + rt.rt_flags |= RTF_WINDOW; + rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX); + continue; + } + +#ifdef RTF_IRTT + if (k == KW_IPVx_IRTT) { + rt.rt_flags |= RTF_IRTT; + rt.rt_irtt = bb_xgetularg10(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_and_die("bad irtt"); + } +#endif + continue; + } +#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; + } + + /* Nothing matched. */ + bb_show_usage(); + } + +#ifdef RTF_REJECT + if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) { + rt.rt_dev = "lo"; + } +#endif + + /* sanity checks.. */ + if (mask_in_addr(rt)) { + unsigned long mask = mask_in_addr(rt); + + mask = ~ntohl(mask); + if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) { + bb_error_msg_and_die("netmask %.8x and host route conflict", + (unsigned int) mask); + } + if (mask & (mask + 1)) { + 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_and_die("netmask and route address conflict"); + } + } + + /* Fill out netmask if still unset */ + 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) { + bb_perror_msg_and_die("socket"); + } + + if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { + bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); + } + + /* Don't bother closing, as we're exiting after we return anyway. */ + /* close(skfd); */ +} + +#ifdef CONFIG_FEATURE_IPV6 + +static void INET6_setroute(int action, char **args) +{ + struct sockaddr_in6 sa6; + struct in6_rtmsg rt; + int prefix_len, skfd; + const char *devname; + + assert((action == RTACTION_ADD) || (action == RTACTION_DEL)); + + { + /* We know args isn't NULL from the check in route_main. */ + const char *target = *args++; + + if (strcmp(target, "default") == 0) { + prefix_len = 0; + memset(&sa6, 0, sizeof(sa6)); + } else { + char *cp; + if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */ + *cp = 0; + prefix_len = bb_xgetularg10_bnd(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)); + + memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); + + /* Fill in the other fields. */ + 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) { + int k = kw_lookup(tbl_ipvx, &args); + const char *args_m1 = args[-1]; + + if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) { + rt.rtmsg_flags |= flags_ipvx[k & 3]; + continue; + } + + if (k == KW_IPVx_METRIC) { + rt.rtmsg_metric = bb_xgetularg10(args_m1); + continue; + } + + if (k == KW_IPVx_GATEWAY) { + if (rt.rtmsg_flags & RTF_GATEWAY) { + bb_show_usage(); + } + + 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; + continue; + } + + /* 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; + } + + /* Nothing matched. */ + bb_show_usage(); + } + + /* Create a socket to the INET6 kernel. */ + if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("socket"); + } + + 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) { + bb_perror_msg_and_die("SIOGIFINDEX"); + } + rt.rtmsg_ifindex = ifr.ifr_ifindex; + } + + /* Tell the kernel to accept this route. */ + if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { + bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); + } + + /* Don't bother closing, as we're exiting after we return anyway. */ + /* close(skfd); */ +} +#endif + +static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */ + RTF_GATEWAY, + RTF_HOST, + RTF_REINSTATE, + RTF_DYNAMIC, + RTF_MODIFIED, +#ifdef CONFIG_FEATURE_IPV6 + RTF_DEFAULT, + RTF_ADDRCONF, + RTF_CACHE +#endif +}; + +#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) + +static const char flagchars[] = /* Must agree with flagvals[]. */ + "GHRDM" +#ifdef CONFIG_FEATURE_IPV6 + "DAC" +#endif +; + +static +#ifndef CONFIG_FEATURE_IPV6 +__inline +#endif +void set_flags(char *flagstr, int flags) +{ + int i; + + *flagstr++ = 'U'; + + for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) { + if (flags & flagvals[i]) { + ++flagstr; + } + } +} + +void displayroutes(int noresolve, int netstatfmt) +{ + char devname[64], flags[16], sdest[16], sgw[16]; + unsigned long int d, g, m; + int flgs, ref, use, metric, mtu, win, ir; + struct sockaddr_in s_addr; + struct in_addr mask; + + FILE *fp = bb_xfopen("/proc/net/route", "r"); + + bb_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; + INET_rresolve(sdest, sizeof(sdest), &s_addr, + (noresolve | 0x8000), m); /* Default instead of *. */ + + s_addr.sin_addr.s_addr = g; + INET_rresolve(sgw, sizeof(sgw), &s_addr, + (noresolve | 0x4000), m); /* Host instead of net. */ + + mask.s_addr = m; + bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags); + if (netstatfmt) { + bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname); + } else { + bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname); + } + } +} + +#ifdef CONFIG_FEATURE_IPV6 + +static void INET6_displayroutes(int noresolve) +{ + char addr6[128], naddr6[128]; + /* 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]; + int iflags, metric, refcnt, use, prefix_len, slen; + struct sockaddr_in6 snaddr6; + + FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r"); + + bb_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"); + } + + /* 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; + } + + 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; + INET6_rresolve(naddr6, sizeof(naddr6), + (struct sockaddr_in6 *) &snaddr6, +#if 0 + (noresolve | 0x8000) /* Default instead of *. */ +#else + 0x0fff /* Apparently, upstream never resolves. */ +#endif + ); + + if (!r) { /* 1st pass */ + snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len); + r += 40; + } else { /* 2nd pass */ + /* Print the info. */ + bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n", + addr6, naddr6, flags, metric, refcnt, use, iface); + break; + } + } while (1); + } +} + +#endif + +#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. */ +static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */ + "\006\001add\0" + "\006\002del\0" +/* "\011\002delete\0" */ + "\010\002delete" /* Since last, we can save a byte. */ +; + +int route_main(int argc, char **argv) +{ + unsigned long opt; + int what; + char *family; + + /* First, remap '-net' and '-host' to avoid getopt problems. */ + { + char **p = argv; + + while (*++p) { + if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) { + p[0][0] = '#'; + } + } + } + + opt = bb_getopt_ulflags(argc, argv, "A:ne", &family); + + if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) { +#ifdef CONFIG_FEATURE_IPV6 + if (strcmp(family, "inet6") == 0) { + opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */ + } else +#endif + bb_show_usage(); + } + + argv += optind; + + /* No more args means display the routing table. */ + if (!*argv) { + int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0; +#ifdef CONFIG_FEATURE_IPV6 + if (opt & ROUTE_OPT_INET6) + INET6_displayroutes(noresolve); + else +#endif + displayroutes(noresolve, opt & ROUTE_OPT_e); + + bb_xferror_stdout(); + bb_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 (opt & ROUTE_OPT_INET6) + INET6_setroute(what, argv); + else +#endif + INET_setroute(what, argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/networking/telnet.c b/busybox/networking/telnet.c new file mode 100644 index 0000000..6ad7712 --- /dev/null +++ b/busybox/networking/telnet.c @@ -0,0 +1,769 @@ +/* vi: set sw=4 ts=4: */ +/* + * telnet implementation for busybox + * + * Author: Tomi Ollila <too@iki.fi> + * Copyright (C) 1994-2000 by Tomi Ollila + * + * 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 + * + * HISTORY + * Revision 3.1 1994/04/17 11:31:54 too + * initial revision + * 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" + +#if 0 +static const int DOTRACE = 1; +#endif + +#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> +#endif + +#define DATABUFSIZE 128 +#define IACBUFSIZE 128 + +static const int CHM_TRY = 0; +static const int CHM_ON = 1; +static const int CHM_OFF = 2; + +static const int UF_ECHO = 0x01; +static const int UF_SGA = 0x02; + +enum { + TS_0 = 1, + TS_IAC = 2, + TS_OPT = 3, + TS_SUB1 = 4, + 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 */ + byte telstate; /* telnet negotiation state from network input */ + byte telwish; /* DO, DONT, WILL, WONT */ + byte charmode; + byte telflags; + byte gotsig; + /* 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; +} + +/* Function prototypes */ +static void rawmode(void); +static void cookmode(void); +static void do_linemode(void); +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 + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static const char *autologin; +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static int win_width, win_height; +#endif + +static void doexit(int ev) +{ + cookmode(); + exit(ev); +} + +static void conescape(void) +{ + char b; + + if (G.gotsig) /* came from line mode... go raw */ + rawmode(); + + WriteCS(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); + + switch (b) + { + case 'l': + if (!G.gotsig) + { + do_linemode(); + goto rrturn; + } + break; + case 'c': + if (G.gotsig) + { + will_charmode(); + goto rrturn; + } + break; + case 'z': + cookmode(); + kill(0, SIGTSTP); + rawmode(); + break; + case 'e': + doexit(0); + } + + WriteCS(1, "continuing...\r\n"); + + if (G.gotsig) + cookmode(); + + rrturn: + G.gotsig = 0; + +} +static void handlenetoutput(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 + * + * 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. + */ + + int i, j; + byte * p = G.buf; + byte outbuf[4*DATABUFSIZE]; + + for (i = len, j = 0; i > 0; i--, p++) + { + if (*p == 0x1d) + { + conescape(); + return; + } + outbuf[j++] = *p; + if (*p == 0xff) + outbuf[j++] = 0xff; + else if (*p == 0x0d) + outbuf[j++] = 0x00; + } + if (j > 0 ) + write(G.netfd, outbuf, j); +} + + +static void handlenetinput(int len) +{ + int i; + int cstart = 0; + + for (i = 0; i < len; i++) + { + byte c = G.buf[i]; + + if (G.telstate == 0) /* most of the time state == 0 */ + { + if (c == IAC) + { + cstart = i; + G.telstate = TS_IAC; + } + } + 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; + + len = cstart; + } + + if (len) + write(1, G.buf, len); +} + + +/* ******************************* */ + +static inline void putiac(int c) +{ + G.iacbuf[G.iaclen++] = c; +} + + +static void putiac2(byte wwdd, byte c) +{ + if (G.iaclen + 3 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(wwdd); + putiac(c); +} + +#if 0 +static void putiac1(byte c) +{ + if (G.iaclen + 2 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(c); +} +#endif + +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static void putiac_subopt(byte c, char *str) +{ + int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) + + if (G.iaclen + len > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(c); + putiac(0); + + while(*str) + putiac(*str++); + + putiac(IAC); + putiac(SE); +} +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static void putiac_subopt_autologin(void) +{ + int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2) + char *user = "USER"; + + if (G.iaclen + len > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(TELOPT_NEW_ENVIRON); + putiac(TELQUAL_IS); + putiac(NEW_ENV_VAR); + + while(*user) + putiac(*user++); + + putiac(NEW_ENV_VALUE); + + while(*autologin) + putiac(*autologin++); + + putiac(IAC); + putiac(SE); +} +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static void putiac_naws(byte c, int x, int y) +{ + if (G.iaclen + 9 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(c); + + putiac((x >> 8) & 0xff); + putiac(x & 0xff); + putiac((y >> 8) & 0xff); + putiac(y & 0xff); + + putiac(IAC); + putiac(SE); +} +#endif + +/* void putiacstring (subneg strings) */ + +/* ******************************* */ + +static char const escapecharis[] = "\r\nEscape character is "; + +static void setConMode(void) +{ + 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 + { + if (G.charmode != CHM_OFF) { + G.charmode = CHM_OFF; + printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis); + cookmode(); + } + } +} + +/* ******************************* */ + +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(); +} + +static void do_linemode(void) +{ + G.charmode = CHM_TRY; + G.telflags &= ~(UF_ECHO | UF_SGA); + setConMode(); + + putiac2(DONT, TELOPT_ECHO); + putiac2(DONT, TELOPT_SGA); + iacflush(); +} + +/* ******************************* */ + +static inline void to_notsup(char c) +{ + if (G.telwish == WILL) putiac2(DONT, c); + else if (G.telwish == DO) putiac2(WONT, c); +} + +static inline 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; + } + else + if (G.telwish == WONT) + return; + + if (G.charmode != CHM_OFF) + G.telflags ^= UF_ECHO; + + if (G.telflags & UF_ECHO) + putiac2(DO, TELOPT_ECHO); + else + putiac2(DONT, TELOPT_ECHO); + + setConMode(); + WriteCS(1, "\r\n"); /* sudden modec */ +} + +static inline void to_sga(void) +{ + /* daemon always sends will/wont, client do/dont */ + + 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); + + return; +} + +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static inline void to_ttype(void) +{ + /* Tell server we will (or won't) do TTYPE */ + + if(ttype) + putiac2(WILL, TELOPT_TTYPE); + else + putiac2(WONT, TELOPT_TTYPE); + + return; +} +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static inline void to_new_environ(void) +{ + /* Tell server we will (or will not) do AUTOLOGIN */ + + if (autologin) + putiac2(WILL, TELOPT_NEW_ENVIRON); + else + putiac2(WONT, TELOPT_NEW_ENVIRON); + + return; +} +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static inline void to_naws(void) +{ + /* Tell server we will do NAWS */ + putiac2(WILL, TELOPT_NAWS); + return; +} +#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; +#endif +#ifdef CONFIG_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; +#endif + default: to_notsup(c); + break; + } +} + + +/* ******************************* */ + +/* subnegotiation -- ignore all (except TTYPE,NAWS) */ + +static int subneg(byte c) +{ + switch (G.telstate) + { + case TS_SUB1: + if (c == IAC) + G.telstate = TS_SUB2; +#ifdef CONFIG_FEATURE_TELNET_TTYPE + else + if (c == TELOPT_TTYPE) + putiac_subopt(TELOPT_TTYPE,ttype); +#endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + else + if (c == TELOPT_NEW_ENVIRON) + putiac_subopt_autologin(); +#endif + break; + case TS_SUB2: + if (c == SE) + return TRUE; + G.telstate = TS_SUB1; + /* break; */ + } + return FALSE; +} + +/* ******************************* */ + +static void fgotsig(int sig) +{ + G.gotsig = sig; +} + + +static void rawmode(void) +{ + tcsetattr(0, TCSADRAIN, &G.termios_raw); +} + +static void cookmode(void) +{ + tcsetattr(0, TCSADRAIN, &G.termios_def); +} + +extern int telnet_main(int argc, char** argv) +{ + int len; + struct sockaddr_in s_in; +#ifdef USE_POLL + struct pollfd ufds[2]; +#else + fd_set readfds; + int maxfd; +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + int opt; +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH + get_terminal_width_height(0, &win_width, &win_height); +#endif + +#ifdef CONFIG_FEATURE_TELNET_TTYPE + ttype = getenv("TERM"); +#endif + + memset(&G, 0, sizeof G); + + 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(); + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + autologin = NULL; + while ((opt = getopt(argc, argv, "al:")) != EOF) { + switch (opt) { + case 'l': + autologin = optarg; + break; + case 'a': + autologin = getenv("USER"); + break; + case '?': + bb_show_usage(); + break; + } + } + if (optind < argc) { + bb_lookup_host(&s_in, argv[optind++]); + s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] : + "telnet", "tcp", 23); + if (optind < argc) + bb_show_usage(); + } else + bb_show_usage(); +#else + bb_lookup_host(&s_in, argv[1]); + s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23); +#endif + + G.netfd = xconnect(&s_in); + + setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); + + signal(SIGINT, fgotsig); + +#ifdef USE_POLL + ufds[0].fd = 0; ufds[1].fd = G.netfd; + ufds[0].events = ufds[1].events = POLLIN; +#else + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(G.netfd, &readfds); + maxfd = G.netfd + 1; +#endif + + while (1) + { +#ifndef USE_POLL + fd_set rfds = readfds; + + switch (select(maxfd, &rfds, NULL, NULL, NULL)) +#else + switch (poll(ufds, 2, -1)) +#endif + { + case 0: + /* timeout */ + case -1: + /* error, ignore and/or log something, bay go to loop */ + if (G.gotsig) + conescape(); + else + sleep(1); + break; + default: + +#ifdef USE_POLL + if (ufds[0].revents) /* well, should check POLLIN, but ... */ +#else + if (FD_ISSET(0, &rfds)) +#endif + { + len = read(0, G.buf, DATABUFSIZE); + + if (len <= 0) + doexit(0); + + TRACE(0, ("Read con: %d\n", len)); + + handlenetoutput(len); + } + +#ifdef USE_POLL + if (ufds[1].revents) /* well, should check POLLIN, but ... */ +#else + if (FD_ISSET(G.netfd, &rfds)) +#endif + { + len = read(G.netfd, G.buf, DATABUFSIZE); + + if (len <= 0) + { + WriteCS(1, "Connection closed by foreign host.\r\n"); + doexit(1); + } + TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); + + handlenetinput(len); + } + } + } +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/networking/telnetd.c b/busybox/networking/telnetd.c new file mode 100644 index 0000000..491c66f --- /dev/null +++ b/busybox/networking/telnetd.c @@ -0,0 +1,660 @@ +/* $Id: telnetd.c,v 1.13 2004/09/14 17:24:58 bug1 Exp $ + * + * 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. + * + * --------------------------------------------------------------------------- + * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN + **************************************************************************** + * + * The telnetd manpage says it all: + * + * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for + * a client, then creating a login process which has the slave side of the + * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the + * master side of the pseudo-terminal, implementing the telnet protocol and + * passing characters between the remote client and the login process. + * + * Vladimir Oleynik <dzo@simtreas.ru> 2001 + * Set process group corrections, initial busybox port + */ + +/*#define DEBUG 1 */ + +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <sys/ioctl.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 TELCMDS +#define TELOPTS +#endif +#include <arpa/telnet.h> +#include <ctype.h> +#include <sys/syslog.h> + +#include "busybox.h" + +#define BUFSIZE 4000 + +#ifdef CONFIG_LOGIN +static const char *loginpath = "/bin/login"; +#else +static const char *loginpath; +#endif +static const char *issuefile = "/etc/issue.net"; + +/* shell name and arguments */ + +static const char *argv_init[] = {NULL, NULL}; + +/* 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; + /* two circular buffers */ + char *buf1, *buf2; + 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. + +*/ + +static int maxfd; + +static struct tsession *sessions; + + +/* + + 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. + + 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. + + 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; + 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) { + int c = *ptr; + *totty++ = *ptr++; + /* We now map \r\n ==> \r for pragmatic reasons. + * Many client implementations send \r\n when + * the user hits the CarriageReturn key. + */ + if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) + ptr++; + } + else { + /* + * 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 + */ + else 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]; + (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); + ptr += 9; + } + else { + /* skip 3-byte IAC non-SB cmd */ +#ifdef 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. */ + return memmove(ptr - num_totty, ptr0, num_totty); +} + + +static int +getpty(char *line) +{ + 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) { + 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; + } + } + } +#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; +} + + +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 */ +{ + 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(LOG_ERR, "All network ports in use!"); + return 0; + } + + if (pty > maxfd) + maxfd = pty; + + ts->ptyfd = pty; + + /* 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_NAWS); + send_iac(ts, DO, TELOPT_LFLOW); + send_iac(ts, WILL, TELOPT_ECHO); + send_iac(ts, WILL, TELOPT_SGA); + + + if ((pid = fork()) < 0) { + syslog(LOG_ERR, "Can`t forking"); + } + 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(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); + + /* NOT REACHED */ + syslog(LOG_ERR, "execv error"); + exit(1); + } + + ts->shell_pid = pid; + + return ts; +} + +#ifndef CONFIG_FEATURE_TELNETD_INETD +static void +free_session(struct tsession *ts) +{ + struct tsession *t = sessions; + + /* Unlink this telnet session from the session list. */ + if(t == ts) + sessions = ts->next; + else { + while(t->next != ts) + t = t->next; + t->next = ts->next; + } + + kill(ts->shell_pid, SIGKILL); + + wait4(ts->shell_pid, NULL, 0, NULL); + + close(ts->ptyfd); + close(ts->sockfd); + + if(ts->ptyfd == maxfd || ts->sockfd == maxfd) + maxfd--; + if(ts->ptyfd == maxfd || ts->sockfd == maxfd) + maxfd--; + + 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; + +#ifndef CONFIG_LOGIN + loginpath = DEFAULT_SHELL; +#endif + + for (;;) { + c = getopt( argc, argv, options); + if (c == EOF) break; + switch (c) { + case 'f': + issuefile = optarg; + break; + case 'l': + loginpath = optarg; + break; +#ifndef CONFIG_FEATURE_TELNETD_INETD + case 'p': + portnbr = atoi(optarg); + break; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + default: + bb_show_usage(); + } + } + + if (access(loginpath, X_OK) < 0) { + bb_error_msg_and_die ("'%s' unavailable.", loginpath); + } + + argv_init[0] = loginpath; + + openlog(bb_applet_name, 0, LOG_USER); + +#ifdef CONFIG_FEATURE_TELNETD_INETD + maxfd = 1; + sessions = make_new_session(); +#else /* CONFIG_EATURE_TELNETD_INETD */ + sessions = 0; + + /* Grab a TCP socket. */ + + master_fd = socket(AF_INET, SOCK_STREAM, 0); + if (master_fd < 0) { + bb_perror_msg_and_die("socket"); + } + (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"); + } + + if (listen(master_fd, 1) < 0) { + bb_perror_msg_and_die("listen"); + } + + if (daemon(0, 0) < 0) + bb_perror_msg_and_die("daemon"); + + + maxfd = master_fd; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + do { + struct tsession *ts; + + FD_ZERO(&rdfdset); + FD_ZERO(&wrfdset); + + /* select on the master socket, all telnet sockets and their + * ptys if there is room in their respective session buffers. + */ + +#ifndef CONFIG_FEATURE_TELNETD_INETD + FD_SET(master_fd, &rdfdset); +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + 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; + } +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); + + if (!selret) + break; + +#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... */ +#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; + } + +#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->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; + } + + if (ts->size1 == 0) { + ts->rdidx1 = 0; + ts->wridx1 = 0; + } + if (ts->size2 == 0) { + ts->rdidx2 = 0; + ts->wridx2 = 0; + } +#ifndef CONFIG_FEATURE_TELNETD_INETD + ts = next; + } +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + } while (1); + + return 0; +} diff --git a/busybox/networking/tftp.c b/busybox/networking/tftp.c new file mode 100644 index 0000000..3c94731 --- /dev/null +++ b/busybox/networking/tftp.c @@ -0,0 +1,584 @@ +/* ------------------------------------------------------------------------- */ +/* 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 */ + +/* opcodes we support */ + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#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" +}; + +const int tftp_cmd_get = 1; +const int tftp_cmd_put = 2; + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + +static int tftp_blocksize_check(int blocksize, int bufsize) +{ + /* Check if the blocksize 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; + } + + return blocksize; +} + +static char *tftp_option_get(char *buf, int len, char *option) +{ + int opt_val = 0; + int opt_found = 0; + int k; + + while (len > 0) { + + /* Make sure the options are terminated correctly */ + + for (k = 0; k < len; k++) { + if (buf[k] == '\0') { + break; + } + } + + if (k >= len) { + break; + } + + if (opt_val == 0) { + if (strcasecmp(buf, option) == 0) { + 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 unsigned short port, int tftp_bufsize) +{ + 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; + unsigned short block_nr = 1; + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + int want_option_ack = 0; +#endif + + /* 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; + + if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg("socket"); + return EXIT_FAILURE; + } + + len = sizeof(sa); + + memset(&sa, 0, len); + bind(socketfd, (struct sockaddr *)&sa, len); + + sa.sin_family = host->h_addrtype; + sa.sin_port = port; + memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, + sizeof(sa.sin_addr)); + + /* build opcode */ + + if (cmd_get) { + opcode = TFTP_RRQ; + } + + if (cmd_put) { + opcode = TFTP_WRQ; + } + + while (1) { + + cp = buf; + + /* first create the opcode part */ + + *((unsigned short *) cp) = htons(opcode); + + 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; + } + + 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 = bb_full_read(localfd, cp, tftp_bufsize - 4); + + if (len < 0) { + bb_perror_msg("read"); + break; + } + + if (len != (tftp_bufsize - 4)) { + finished++; + } + + cp += len; + } + } + + + /* send packet */ + + + timeout = bb_tftp_num_retries; /* re-initialize */ + do { + + len = cp - buf; + +#ifdef CONFIG_FEATURE_TFTP_DEBUG + fprintf(stderr, "sending %u bytes\n", len); + for (cp = buf; cp < &buf[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; + } + + + if (finished && (opcode == TFTP_ACK)) { + break; + } + + /* 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 == 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; + } + + } 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 + fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); +#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); + } + + break; + } + +#ifdef CONFIG_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 + fprintf(stderr, "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; + } +#endif + + if (cmd_get && (opcode == TFTP_DATA)) { + + if (tmp == block_nr) { + + len = bb_full_write(localfd, &buf[4], len - 4); + + if (len < 0) { + bb_perror_msg("write"); + break; + } + + if (len != (tftp_bufsize - 4)) { + finished++; + } + + opcode = TFTP_ACK; + continue; + } + } + + if (cmd_put && (opcode == TFTP_ACK)) { + + if (tmp == (unsigned short)(block_nr - 1)) { + if (finished) { + break; + } + + opcode = TFTP_DATA; + continue; + } + } + } + +#ifdef CONFIG_FEATURE_CLEAN_UP + close(socketfd); + + free(buf); +#endif + + return finished ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int tftp_main(int argc, char **argv) +{ + struct hostent *host = NULL; + const char *localfile = NULL; + const char *remotefile = NULL; + int port; + int cmd = 0; + int fd = -1; + int flags = 0; + int opt; + int result; + int blocksize = TFTP_BLOCKSIZE_DEFAULT; + + /* figure out what to pass to getopt */ + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE +#define BS "b:" +#else +#define BS +#endif + +#ifdef CONFIG_FEATURE_TFTP_GET +#define GET "g" +#else +#define GET +#endif + +#ifdef CONFIG_FEATURE_TFTP_PUT +#define PUT "p" +#else +#define PUT +#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 | O_TRUNC; + break; +#endif +#ifdef CONFIG_FEATURE_TFTP_PUT + case 'p': + cmd = tftp_cmd_put; + flags = O_RDONLY; + break; +#endif + case 'l': + localfile = optarg; + break; + case 'r': + remotefile = optarg; + break; + } + } + + if ((cmd == 0) || (optind == argc)) { + bb_show_usage(); + } + if(localfile && strcmp(localfile, "-") == 0) { + fd = fileno((cmd==tftp_cmd_get)? stdout : stdin); + } + if(localfile == NULL) + localfile = remotefile; + if(remotefile == NULL) + remotefile = localfile; + if (fd==-1) { + fd = open(localfile, flags, 0644); + } + if (fd < 0) { + bb_perror_msg_and_die("local file"); + } + + host = xgethostbyname(argv[optind]); + port = bb_lookup_port(argv[optind + 1], "udp", 69); + +#ifdef CONFIG_FEATURE_TFTP_DEBUG + fprintf(stderr, "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 == STDOUT_FILENO || fd == STDIN_FILENO)) { + close(fd); + } +#endif + return(result); +} diff --git a/busybox/networking/traceroute.c b/busybox/networking/traceroute.c new file mode 100644 index 0000000..44ffdf0 --- /dev/null +++ b/busybox/networking/traceroute.c @@ -0,0 +1,548 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * 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. + */ + +/* + * traceroute host - trace the route ip packets follow going to "host". + * 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.) + * + * 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 + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * 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) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#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> +#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 +#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 */ +}; + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ +#define IPVERSION 4 + + +#include "busybox.h" + +static u_char packet[512]; /* last inbound (icmp) packet */ +static struct opacket *outpacket; /* last output (udp) packet */ + +static int s; /* receive (icmp) socket file descriptor */ +static int sndsock; /* send (udp) socket file descriptor */ + +static struct sockaddr whereto; /* Who to try to reach */ +static int datalen; /* How much data */ + +static char *hostname; + +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 */ + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +static inline void +inetname(struct sockaddr_in *from) +{ + 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; + } + 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; + + inetname(from); +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + if (verbose) + printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst)); +#endif +} + +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) +{ + fd_set fds; + static struct timeval wait; + int cc = 0; + int fromlen = sizeof (*from); + + 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; + } + + 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); + + return(cc); +} + +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE +/* + * Convert an ICMP "type" field to a printable string. + */ +static inline const char * +pr_type(u_char t) +{ + 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", + "Param Problem", "Timestamp", "Timestamp Reply", "Info Request", + "Info Reply" + }; + + if(t > 16) + return("OUT-OF-RANGE"); + + return(ttab[t]); +} +#endif + +static inline int +packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq) +{ + struct icmp *icp; + u_char type, code; + int hlen; + struct ip *ip; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + if (verbose) + printf("packet too short (%d bytes) from %s\n", cc, + inet_ntoa(from->sin_addr)); +#endif + 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; + + 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); + } +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + if (verbose) { + int i; + u_long *lp = (u_long *)&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), + type, pr_type(type), icp->icmp_code); + for (i = 4; i < cc ; i += sizeof(long)) + printf("%2d: x%8.8lx\n", i, *lp++); + } +#endif + return(0); +} + +static void /* not inline */ +send_probe(int seq, int ttl) +{ + 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); + } +} + + +int +#ifndef CONFIG_TRACEROUTE +main(int argc, char *argv[]) +#else +traceroute_main(int argc, char *argv[]) +#endif +{ + 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; + int nprobes = 3; + + 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; +#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++; +#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(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + bb_show_usage(); + + 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)); +#endif + if (options & SO_DONTROUTE) + (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); +#ifdef SO_SNDBUF + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, + sizeof(datalen)) < 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"); +#endif +#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG + if (options & SO_DEBUG) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); +#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"); +#endif + } + + 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); + + for (ttl = 1; ttl <= max_ttl; ++ttl) { + u_long lastaddr = 0; + int got_there = 0; + int unreachable = 0; + + printf("%2d ", ttl); + for (probe = 0; probe < nprobes; ++probe) { + int cc, reset_timer; + struct timeval t1, t2; + struct timezone tz; + struct ip *ip; + + (void) gettimeofday(&t1, &tz); + 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; + } + break; + } else + reset_timer = 0; + } + if (cc == 0) + printf(" *"); + (void) fflush(stdout); + } + putchar('\n'); + if (got_there || unreachable >= nprobes-1) + return 0; + } + + return 0; +} diff --git a/busybox/networking/udhcp/AUTHORS b/busybox/networking/udhcp/AUTHORS new file mode 100644 index 0000000..f3f4336 --- /dev/null +++ b/busybox/networking/udhcp/AUTHORS @@ -0,0 +1,13 @@ +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/) +Vladimir Oleynik <dzo@simtrea.ru> Size optimizations + + diff --git a/busybox/networking/udhcp/COPYING b/busybox/networking/udhcp/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/busybox/networking/udhcp/COPYING @@ -0,0 +1,339 @@ + 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/busybox/networking/udhcp/ChangeLog b/busybox/networking/udhcp/ChangeLog new file mode 100644 index 0000000..bf2982f --- /dev/null +++ b/busybox/networking/udhcp/ChangeLog @@ -0,0 +1,260 @@ +0.9.9 (pending) ++ Various other size optimizations (Vladimir) ++ Change strerror(errno) to %m (Vladimir N. Oleynik <dzo@simtreas.ru>) ++ 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@iinet.net.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 <andersee@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/busybox/networking/udhcp/Config.in b/busybox/networking/udhcp/Config.in new file mode 100644 index 0000000..fc07a9b --- /dev/null +++ b/busybox/networking/udhcp/Config.in @@ -0,0 +1,62 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "udhcp Server/Client" + +config CONFIG_UDHCPD + bool "udhcp Server (udhcpd)" + default n + help + 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)" + default n + 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 lease is obtained or lost. + + See http://udhcp.busybox.net for further details. + +config CONFIG_DUMPLEASES + bool "Lease display utility (dumpleases)" + default n + depends on CONFIG_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 CONFIG_FEATURE_UDHCP_SYSLOG + bool " Log udhcp messages to syslog (instead of stdout)" + 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. + + See http://udhcp.busybox.net for further details. + +config CONFIG_FEATURE_UDHCP_DEBUG + bool " Compile udhcp with noisy debugging messages" + default n + depends on CONFIG_UDHCPD || CONFIG_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. + + See http://udhcp.busybox.net for further details. + +endmenu + diff --git a/busybox/networking/udhcp/Makefile b/busybox/networking/udhcp/Makefile new file mode 100644 index 0000000..3d32db5 --- /dev/null +++ b/busybox/networking/udhcp/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/networking/udhcp +UDHCP_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/networking/udhcp/Makefile.in b/busybox/networking/udhcp/Makefile.in new file mode 100644 index 0000000..94750f6 --- /dev/null +++ b/busybox/networking/udhcp/Makefile.in @@ -0,0 +1,54 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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:=$(top_builddir)/networking/udhcp/ +endif +srcdir=$(top_srcdir)/networking/udhcp + +#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 + +UDHCP-y:= +UDHCP-$(CONFIG_UDHCP_SHARED) += common.c options.c packet.c pidfile.c \ + signalpipe.c socket.c +UDHCP-$(CONFIG_UDHCPC) += dhcpc.c clientpacket.c clientsocket.c \ + script.c +UDHCP-$(CONFIG_UDHCPD) += dhcpd.c arpping.c files.c leases.c \ + serverpacket.c static_leases.c +UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.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): $(UDHCP_DIR)%.o : $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DIN_BUSYBOX -c $< -o $@ + diff --git a/busybox/networking/udhcp/README b/busybox/networking/udhcp/README new file mode 100644 index 0000000..dd99294 --- /dev/null +++ b/busybox/networking/udhcp/README @@ -0,0 +1,53 @@ +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: + + UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra + debugging output, compile with -g, and not fork to the background when + run. + UDHCP_SYSLOG: If UDHCP_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 three compile time options: + + LEASE_TIME: The default lease time if not specified in the config + file. + + LEASES_FILE: The default file for storing leases. + + 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/busybox/networking/udhcp/README.dumpleases b/busybox/networking/udhcp/README.dumpleases new file mode 100644 index 0000000..6367710 --- /dev/null +++ b/busybox/networking/udhcp/README.dumpleases @@ -0,0 +1,17 @@ +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/busybox/networking/udhcp/README.udhcpc b/busybox/networking/udhcp/README.udhcpc new file mode 100644 index 0000000..d720a37 --- /dev/null +++ b/busybox/networking/udhcp/README.udhcpc @@ -0,0 +1,141 @@ +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/busybox/networking/udhcp/README.udhcpd b/busybox/networking/udhcp/README.udhcpd new file mode 100644 index 0000000..169de78 --- /dev/null +++ b/busybox/networking/udhcp/README.udhcpd @@ -0,0 +1,59 @@ +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/busybox/networking/udhcp/TODO b/busybox/networking/udhcp/TODO new file mode 100644 index 0000000..6febe5a --- /dev/null +++ b/busybox/networking/udhcp/TODO @@ -0,0 +1,16 @@ +TODO +---- ++ Check for valid IP, netmask, hostname, paths, strings, etc ++ 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 + * Server notification when a lease is added/removed ++ Additional bootp support in client/server ++ Make serverid option in server configurable ++ Possibly add failure message to DHCP NAK diff --git a/busybox/networking/udhcp/arpping.c b/busybox/networking/udhcp/arpping.c new file mode 100644 index 0000000..7cc2be4 --- /dev/null +++ b/busybox/networking/udhcp/arpping.c @@ -0,0 +1,106 @@ +/* + * arpping.c + * + * Mostly stolen from: dhcpcd - DHCP client daemon + * by Yoichi Hariguchi <yoichi@fore.com> + */ + +#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" + +/* 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(uint32_t yiaddr, uint32_t ip, uint8_t *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; + + + if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) { +#ifdef IN_BUSYBOX + LOG(LOG_ERR, bb_msg_can_not_create_raw_socket); +#else + LOG(LOG_ERR, "Could not open raw socket"); +#endif + 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; + } + + /* send arp request */ + memset(&arp, 0, sizeof(arp)); + memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ + memcpy(arp.h_source, 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.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(&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; + prevTime = uptime(); + 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 && + *((uint32_t *) arp.sInaddr) == yiaddr) { + DEBUG(LOG_INFO, "Valid arp reply receved for this address"); + rv = 0; + break; + } + } + timeout -= uptime() - prevTime; + prevTime = uptime(); + } + close(s); + DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V"); + return rv; +} diff --git a/busybox/networking/udhcp/arpping.h b/busybox/networking/udhcp/arpping.h new file mode 100644 index 0000000..6f27d9f --- /dev/null +++ b/busybox/networking/udhcp/arpping.h @@ -0,0 +1,35 @@ +/* + * 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 { + /* Ethernet header */ + u_char h_dest[6]; /* destination ether addr */ + u_char h_source[6]; /* source ether addr */ + u_short h_proto; /* packet type ID field */ + + /* ARP packet */ + uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */ + uint16_t ptype; /* protocol type (must be ETH_P_IP) */ + uint8_t hlen; /* hardware address length (must be 6) */ + uint8_t plen; /* protocol address length (must be 4) */ + uint16_t operation; /* ARP opcode */ + uint8_t sHaddr[6]; /* sender's hardware address */ + uint8_t sInaddr[4]; /* sender's IP address */ + uint8_t tHaddr[6]; /* target's hardware address */ + uint8_t tInaddr[4]; /* target's IP address */ + uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */ +} __attribute__ ((packed)); + +/* function prototypes */ +int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface); + +#endif diff --git a/busybox/networking/udhcp/clientpacket.c b/busybox/networking/udhcp/clientpacket.c new file mode 100644 index 0000000..ec96601 --- /dev/null +++ b/busybox/networking/udhcp/clientpacket.c @@ -0,0 +1,248 @@ +/* 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. + */ + +#include <string.h> +#include <sys/socket.h> +#include <features.h> +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#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 "dhcpd.h" +#include "clientpacket.h" +#include "options.h" +#include "dhcpc.h" +#include "common.h" + + +/* Create a random xid */ +unsigned long random_xid(void) +{ + static int initialized; + if (!initialized) { + int fd; + unsigned long seed; + + 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++; + } + return rand(); +} + + +/* 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); + 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, (uint8_t *) &vendor_id); +} + + +/* 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) +{ + int end = end_option(packet->options); + int i, len = 0; + + packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; + for (i = 0; dhcp_options[i].code; i++) + if (dhcp_options[i].flags & OPTION_REQ) + packet->options[end + OPT_DATA + len++] = dhcp_options[i].code; + packet->options[end + OPT_LEN] = len; + packet->options[end + OPT_DATA + len] = DHCP_END; + +} + + +/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ +int send_discover(unsigned long xid, unsigned long requested) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPDISCOVER); + packet.xid = xid; + 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); +} + + +/* Broadcasts a DHCP request message */ +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) +{ + struct dhcpMessage packet; + struct in_addr addr; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + add_requests(&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); +} + + +/* Unicasts or broadcasts a DHCP renew message */ +int send_renew(unsigned long xid, unsigned long server, unsigned long 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; +} + + +/* Unicasts a DHCP release message */ +int send_release(unsigned long server, unsigned long 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); +} + + +/* 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) +{ + int bytes; + struct udp_dhcp_packet packet; + uint32_t source, dest; + uint16_t check; + + memset(&packet, 0, sizeof(struct udp_dhcp_packet)); + bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); + if (bytes < 0) { + DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); + usleep(500000); /* possible down interface, looping condition */ + return -1; + } + + if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { + DEBUG(LOG_INFO, "message too short, ignoring"); + return -2; + } + + if (bytes < ntohs(packet.ip.tot_len)) { + DEBUG(LOG_INFO, "Truncated packet"); + 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) != (uint16_t) (bytes - sizeof(packet.ip))) { + DEBUG(LOG_INFO, "unrelated/bogus packet"); + return -2; + } + + /* check 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; + } + + /* verify the UDP checksum by replacing the header with a psuedo header */ + source = packet.ip.saddr; + dest = packet.ip.daddr; + 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"); + 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"); + return -2; + } + DEBUG(LOG_INFO, "oooooh!!! got some!"); + return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); + +} diff --git a/busybox/networking/udhcp/clientpacket.h b/busybox/networking/udhcp/clientpacket.h new file mode 100644 index 0000000..8e5441b --- /dev/null +++ b/busybox/networking/udhcp/clientpacket.h @@ -0,0 +1,14 @@ +#ifndef _CLIENTPACKET_H +#define _CLIENTPACKET_H + +#include "packet.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/busybox/networking/udhcp/clientsocket.c b/busybox/networking/udhcp/clientsocket.c new file mode 100644 index 0000000..7c1b6e8 --- /dev/null +++ b/busybox/networking/udhcp/clientsocket.c @@ -0,0 +1,62 @@ +/* + * 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 <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <netinet/in.h> +#include <features.h> +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif + +#include "clientsocket.h" +#include "common.h" + + +int raw_socket(int ifindex) +{ + int fd; + struct sockaddr_ll sock; + + DEBUG(LOG_INFO, "Opening raw socket on ifindex %d", 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; + } + + return fd; +} diff --git a/busybox/networking/udhcp/clientsocket.h b/busybox/networking/udhcp/clientsocket.h new file mode 100644 index 0000000..17a55c1 --- /dev/null +++ b/busybox/networking/udhcp/clientsocket.h @@ -0,0 +1,7 @@ +/* clientsocket.h */ +#ifndef _CLIENTSOCKET_H +#define _CLIENTSOCKET_H + +int raw_socket(int ifindex); + +#endif diff --git a/busybox/networking/udhcp/common.c b/busybox/networking/udhcp/common.c new file mode 100644 index 0000000..bf2ac44 --- /dev/null +++ b/busybox/networking/udhcp/common.c @@ -0,0 +1,162 @@ +/* common.c + * + * Functions for debugging and logging as well as some other + * simple helper functions. + * + * Russ Dill <Russ.Dill@asu.edu> 2001-2003 + * 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. + */ + +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <paths.h> +#include <sys/socket.h> +#include <stdarg.h> + +#include "common.h" +#include "pidfile.h" + + +static int daemonized; + +long uptime(void) +{ + struct sysinfo info; + sysinfo(&info); + return info.uptime; +} + + +/* + * This function makes sure our first socket calls + * aren't going to fd 1 (printf badness...) and are + * not later closed by daemon() + */ +static inline void sanitize_fds(void) +{ + int zero; + if ((zero = open(_PATH_DEVNULL, O_RDWR, 0)) < 0) return; + while (zero < 3) zero = dup(zero); + close(zero); +} + + +void background(const char *pidfile) +{ +#ifdef __uClinux__ + LOG(LOG_ERR, "Cannot background in uclinux (yet)"); +#else /* __uClinux__ */ + int pid_fd; + + /* hold lock during fork. */ + pid_fd = pidfile_acquire(pidfile); + if (daemon(0, 0) == -1) { + perror("fork"); + exit(1); + } + daemonized++; + pidfile_write_release(pid_fd); +#endif /* __uClinux__ */ +} + + +#ifdef UDHCP_SYSLOG +void udhcp_logging(int level, const char *fmt, ...) +{ + va_list p; + va_list p2; + + va_start(p, fmt); + __va_copy(p2, p); + if(!daemonized) { + vprintf(fmt, p); + putchar('\n'); + } + vsyslog(level, fmt, p2); + va_end(p); +} + + +void start_log_and_pid(const char *client_server, const char *pidfile) +{ + int pid_fd; + + /* Make sure our syslog fd isn't overwritten */ + sanitize_fds(); + + /* do some other misc startup stuff while we are here to save bytes */ + pid_fd = pidfile_acquire(pidfile); + pidfile_write_release(pid_fd); + + /* equivelent of doing a fflush after every \n */ + setlinebuf(stdout); + + openlog(client_server, 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" +}; + + +void udhcp_logging(int level, const char *fmt, ...) +{ + va_list p; + + va_start(p, fmt); + if(!daemonized) { + printf("%s, ", syslog_level_msg[level]); + vprintf(fmt, p); + putchar('\n'); + } + va_end(p); +} + + +void start_log_and_pid(const char *client_server, const char *pidfile) +{ + int pid_fd; + + /* Make sure our syslog fd isn't overwritten */ + sanitize_fds(); + + /* do some other misc startup stuff while we are here to save bytes */ + pid_fd = pidfile_acquire(pidfile); + pidfile_write_release(pid_fd); + + /* equivelent of doing a fflush after every \n */ + setlinebuf(stdout); + + udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION); +} +#endif + diff --git a/busybox/networking/udhcp/common.h b/busybox/networking/udhcp/common.h new file mode 100644 index 0000000..ca19a24 --- /dev/null +++ b/busybox/networking/udhcp/common.h @@ -0,0 +1,56 @@ +/* common.h + * + * 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. + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#include "version.h" +#include "libbb_udhcp.h" + + +#ifndef UDHCP_SYSLOG +enum syslog_levels { + LOG_EMERG = 0, + LOG_ALERT, + LOG_CRIT, + LOG_WARNING, + LOG_ERR, + LOG_INFO, + LOG_DEBUG +}; +#else +#include <syslog.h> +#endif + +long uptime(void); +void background(const char *pidfile); +void start_log_and_pid(const char *client_server, const char *pidfile); +void background(const char *pidfile); +void udhcp_logging(int level, const char *fmt, ...); + +#define LOG(level, str, args...) udhcp_logging(level, str, ## args) + +#ifdef UDHCP_DEBUG +# define DEBUG(level, str, args...) LOG(level, str, ## args) +#else +# define DEBUG(level, str, args...) do {;} while(0) +#endif + +#endif diff --git a/busybox/networking/udhcp/dhcpc.c b/busybox/networking/udhcp/dhcpc.c new file mode 100644 index 0000000..449b517 --- /dev/null +++ b/busybox/networking/udhcp/dhcpc.c @@ -0,0 +1,517 @@ +/* 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. + */ + +#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 "dhcpd.h" +#include "dhcpc.h" +#include "options.h" +#include "clientpacket.h" +#include "clientsocket.h" +#include "script.h" +#include "socket.h" +#include "common.h" +#include "signalpipe.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; + +#define LISTEN_NONE 0 +#define LISTEN_KERNEL 1 +#define LISTEN_RAW 2 +static int listen_mode; + +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 */ +}; + +#ifndef IN_BUSYBOX +static void __attribute__ ((noreturn)) show_usage(void) +{ + printf( +"Usage: udhcpc [OPTIONS]\n\n" +" -c, --clientid=CLIENTID Client identifier\n" +" -H, --hostname=HOSTNAME Client hostname\n" +" -h Alias for -H\n" +" -f, --foreground Do not fork after getting lease\n" +" -b, --background Fork to background if lease cannot be\n" +" immediately negotiated.\n" +" -i, --interface=INTERFACE Interface to use (default: eth0)\n" +" -n, --now Exit with failure if lease cannot be\n" +" immediately negotiated.\n" +" -p, --pidfile=file Store process ID of daemon in file\n" +" -q, --quit Quit after obtaining lease\n" +" -r, --request=IP IP address to request (default: none)\n" +" -s, --script=file Run file at dhcp events (default:\n" +" " DEFAULT_SCRIPT ")\n" +" -v, --version Display version\n" + ); + exit(0); +} +#else +#define show_usage bb_show_usage +extern void show_usage(void) __attribute__ ((noreturn)); +#endif + + +/* just a little helper */ +static void change_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; + listen_mode = new_mode; +} + + +/* perform a renew */ +static void perform_renew(void) +{ + LOG(LOG_INFO, "Performing a DHCP renew"); + switch (state) { + case BOUND: + change_mode(LISTEN_KERNEL); + case RENEWING: + case REBINDING: + state = RENEW_REQUESTED; + break; + case RENEW_REQUESTED: /* impatient are we? fine, square 1 */ + run_script(NULL, "deconfig"); + case REQUESTING: + case RELEASED: + change_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) +{ + char buffer[16]; + 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)); + temp_addr.s_addr = requested_ip; + LOG(LOG_INFO, "Unicasting a release of %s to %s", + inet_ntoa(temp_addr), buffer); + send_release(server_addr, requested_ip); /* unicast */ + run_script(NULL, "deconfig"); + } + LOG(LOG_INFO, "Entering released state"); + + change_mode(LISTEN_NONE); + state = RELEASED; + timeout = 0x7fffffff; +} + + +static void client_background(void) +{ + background(client_config.pidfile); + client_config.foreground = 1; /* Do not fork again. */ + client_config.background_if_no_lease = 0; +} + + +#ifdef COMBINED_BINARY +int udhcpc_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + uint8_t *temp, *message; + unsigned long t1 = 0, t2 = 0, xid = 0; + unsigned long start = 0, lease; + fd_set rfds; + int retval; + struct timeval tv; + int c, len; + struct dhcpMessage packet; + struct in_addr temp_addr; + long 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} + }; + + /* 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': + printf("udhcpcd, version %s\n\n", VERSION); + return 0; + break; + default: + show_usage(); + } + } + + /* Start the log, sanitize fd's, and write a pid file */ + start_log_and_pid("udhcpc", client_config.pidfile); + + 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; + client_config.clientid[OPT_DATA] = 1; + memcpy(client_config.clientid + 3, client_config.arp, 6); + } + + /* setup the signal pipe */ + udhcp_sp_setup(); + + state = INIT_SELECTING; + run_script(NULL, "deconfig"); + change_mode(LISTEN_RAW); + + for (;;) { + + tv.tv_sec = timeout - uptime(); + tv.tv_usec = 0; + + 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; + } + } + max_fd = udhcp_sp_fd_set(&rfds, fd); + + if (tv.tv_sec > 0) { + DEBUG(LOG_INFO, "Waiting on select..."); + retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); + } else retval = 0; /* If we already timed out, fall through */ + + now = uptime(); + if (retval == 0) { + /* timeout dropped to zero */ + switch (state) { + case INIT_SELECTING: + if (packet_num < 3) { + if (packet_num == 0) + xid = random_xid(); + + /* send discover packet */ + send_discover(xid, requested_ip); /* broadcast */ + + timeout = now + ((packet_num == 2) ? 4 : 2); + packet_num++; + } else { + run_script(NULL, "leasefail"); + 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; + } + break; + case RENEW_REQUESTED: + 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); + 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); + } + break; + case BOUND: + /* Lease is starting to run out, time to enter renewing state */ + state = RENEWING; + change_mode(LISTEN_KERNEL); + DEBUG(LOG_INFO, "Entering renew state"); + /* fall right through */ + 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; + } + break; + 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 { + /* send a request packet */ + send_renew(xid, 0, requested_ip); /* broadcast */ + + t2 = (lease - t2) / 2 + t2; + timeout = t2 + start; + } + break; + case RELEASED: + /* yah, I know, *you* say it would never happen */ + timeout = 0x7fffffff; + break; + } + } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { + /* 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; + } + + if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); + continue; + } + + 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; + packet_num = 0; + } else { + DEBUG(LOG_ERR, "No server ID in message"); + } + } + 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; + 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 */ + } + break; + /* case BOUND, RELEASED: - ignore all packets */ + } + } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) { + switch (sig) { + case SIGUSR1: + perform_renew(); + break; + case SIGUSR2: + perform_release(); + break; + case SIGTERM: + LOG(LOG_INFO, "Received SIGTERM"); + return 0; + } + } else if (retval == -1 && errno == EINTR) { + /* a signal was caught */ + } else { + /* An error occured */ + DEBUG(LOG_ERR, "Error on select"); + } + + } + return 0; +} diff --git a/busybox/networking/udhcp/dhcpc.h b/busybox/networking/udhcp/dhcpc.h new file mode 100644 index 0000000..9c4aa95 --- /dev/null +++ b/busybox/networking/udhcp/dhcpc.h @@ -0,0 +1,37 @@ +/* dhcpc.h */ +#ifndef _DHCPC_H +#define _DHCPC_H + +#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" + +/* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */ +#include "libbb_udhcp.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 + + +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 */ + uint8_t *clientid; /* Optional client id to use */ + uint8_t *hostname; /* Optional hostname to use */ + int ifindex; /* Index number of the interface to use */ + uint8_t arp[6]; /* Our arp address */ +}; + +extern struct client_config_t client_config; + + +#endif diff --git a/busybox/networking/udhcp/dhcpd.c b/busybox/networking/udhcp/dhcpd.c new file mode 100644 index 0000000..ab3ddfe --- /dev/null +++ b/busybox/networking/udhcp/dhcpd.c @@ -0,0 +1,273 @@ +/* dhcpd.c + * + * udhcp Server + * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> + * Chris Trew <ctrew@moreton.com.au> + * + * 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. + */ + +#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 "dhcpd.h" +#include "arpping.h" +#include "socket.h" +#include "options.h" +#include "files.h" +#include "serverpacket.h" +#include "common.h" +#include "signalpipe.h" +#include "static_leases.h" + + +/* globals */ +struct dhcpOfferedAddr *leases; +struct server_config_t server_config; + + +#ifdef COMBINED_BINARY +int udhcpd_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + fd_set rfds; + struct timeval tv; + int server_socket = -1; + int bytes, retval; + struct dhcpMessage packet; + uint8_t *state; + uint8_t *server_id, *requested; + uint32_t server_id_align, requested_align; + unsigned long timeout_end; + struct option_set *option; + struct dhcpOfferedAddr *lease; + struct dhcpOfferedAddr static_lease; + int max_sock; + unsigned long num_ips; + + uint32_t static_lease_ip; + + memset(&server_config, 0, sizeof(struct server_config_t)); + read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]); + + /* Start the log, sanitize fd's, and write a pid file */ + start_log_and_pid("udhcpd", server_config.pidfile); + + if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { + memcpy(&server_config.lease, option->data + 2, 4); + 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); + 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); + server_config.max_leases = num_ips; + } + + leases = xcalloc(server_config.max_leases, sizeof(struct dhcpOfferedAddr)); + 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 UDHCP_DEBUG + background(server_config.pidfile); /* hold lock during fork. */ +#endif + + /* Setup the signal pipe */ + udhcp_sp_setup(); + + timeout_end = time(0) + server_config.auto_time; + while(1) { /* loop until universe collapses */ + + 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; + } + + max_sock = udhcp_sp_fd_set(&rfds, server_socket); + if (server_config.auto_time) { + tv.tv_sec = timeout_end - time(0); + tv.tv_usec = 0; + } + if (!server_config.auto_time || tv.tv_sec > 0) { + 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; + continue; + } else if (retval < 0 && errno != EINTR) { + DEBUG(LOG_INFO, "error on select"); + continue; + } + + switch (udhcp_sp_read(&rfds)) { + 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; + case 0: break; /* no signal */ + default: continue; /* signal or error (probably EINTR) */ + } + + if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ + if (bytes == -1 && errno != EINTR) { + DEBUG(LOG_INFO, "error on read, %m, reopening socket"); + 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"); + continue; + } + + /* Look for a static lease */ + static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); + + if(static_lease_ip) + { + printf("Found static lease: %x\n", 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"); + } + break; + case DHCPREQUEST: + DEBUG(LOG_INFO, "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) { + 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); + } + } 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); + } + } + } + + /* 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))) { + 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 { + /* RENEWING or REBINDING State */ + } + break; + case DHCPDECLINE: + DEBUG(LOG_INFO,"received DECLINE"); + if (lease) { + memset(lease->chaddr, 0, 16); + lease->expires = time(0) + server_config.decline_time; + } + break; + case DHCPRELEASE: + DEBUG(LOG_INFO,"received RELEASE"); + if (lease) lease->expires = time(0); + break; + case DHCPINFORM: + DEBUG(LOG_INFO,"received INFORM"); + send_inform(&packet); + break; + default: + LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); + } + } + + return 0; +} + diff --git a/busybox/networking/udhcp/dhcpd.h b/busybox/networking/udhcp/dhcpd.h new file mode 100644 index 0000000..c47f6aa --- /dev/null +++ b/busybox/networking/udhcp/dhcpd.h @@ -0,0 +1,140 @@ +/* dhcpd.h */ +#ifndef _DHCPD_H +#define _DHCPD_H + +#include <netinet/ip.h> +#include <netinet/udp.h> + +#include "libbb_udhcp.h" +#include "leases.h" +#include "version.h" + +/************************************/ +/* Defaults _you_ may want to tweak */ +/************************************/ + +/* 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 "/var/lib/misc/udhcpd.leases" + +/* 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 (uint8_t *) "\xff\xff\xff\xff\xff\xff" +#define OPT_CODE 0 +#define OPT_LEN 1 +#define OPT_DATA 2 + +struct option_set { + uint8_t *data; + struct option_set *next; +}; + +struct static_lease { + uint8_t *mac; + uint32_t *ip; + struct static_lease *next; +}; + +struct server_config_t { + uint32_t server; /* Our IP, in network order */ + uint32_t start; /* Start address of leases, network order */ + uint32_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 */ + uint8_t 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*/ + char *lease_file; + char *pidfile; + char *notify_file; /* What to run whenever leases are written */ + uint32_t siaddr; /* next server bootp option */ + 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 */ +}; + +extern struct server_config_t server_config; +extern struct dhcpOfferedAddr *leases; + + +#endif diff --git a/busybox/networking/udhcp/dumpleases.c b/busybox/networking/udhcp/dumpleases.c new file mode 100644 index 0000000..a9036df --- /dev/null +++ b/busybox/networking/udhcp/dumpleases.c @@ -0,0 +1,110 @@ +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.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> + +#include "dhcpd.h" +#include "leases.h" +#include "libbb_udhcp.h" + +#define REMAINING 0 +#define ABSOLUTE 1 + + +#ifndef IN_BUSYBOX +static void __attribute__ ((noreturn)) show_usage(void) +{ + printf( +"Usage: dumpleases -f <file> -[r|a]\n\n" +" -f, --file=FILENAME Leases file to load\n" +" -r, --remaining Interepret lease times as time remaing\n" +" -a, --absolute Interepret lease times as expire time\n"); + exit(0); +} +#else +#define show_usage bb_show_usage +#endif + + +#ifdef IN_BUSYBOX +int dumpleases_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + FILE *fp; + int i, c, mode = REMAINING; + long expires; + 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} + }; + + 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: + show_usage(); + } + } + + fp = xfopen(file, "r"); + + 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)) { + + for (i = 0; i < 6; i++) { + printf("%02x", lease.chaddr[i]); + if (i != 5) printf(":"); + } + 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)); + } + fclose(fp); + + return 0; +} diff --git a/busybox/networking/udhcp/files.c b/busybox/networking/udhcp/files.c new file mode 100644 index 0000000..73a3bbc --- /dev/null +++ b/busybox/networking/udhcp/files.c @@ -0,0 +1,346 @@ +/* + * files.c -- DHCP server file manipulation * + * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 + */ + +#include <sys/socket.h> +#include <arpa/inet.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> +#include <netdb.h> + +#include <netinet/ether.h> +#include "static_leases.h" + +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "common.h" + +/* + * Domain names may have 254 chars, and string options can be 254 + * chars long. However, 80 bytes will be enough for most, and won't + * hog up memory. If you have a special application, change it + */ +#define READ_CONFIG_BUF_SIZE 80 + +/* on these functions, make sure you 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; +} + +static int read_mac(const char *line, void *arg) +{ + uint8_t *mac_bytes = arg; + struct ether_addr *temp_ether_addr; + int retval = 1; + + temp_ether_addr = ether_aton(line); + + if(temp_ether_addr == NULL) + retval = 0; + else + memcpy(mac_bytes, temp_ether_addr, 6); + + return retval; +} + + +static int read_str(const char *line, void *arg) +{ + char **dest = arg; + + if (*dest) free(*dest); + *dest = strdup(line); + + return 1; +} + + +static int read_u32(const char *line, void *arg) +{ + uint32_t *dest = arg; + char *endptr; + *dest = strtoul(line, &endptr, 0); + return endptr[0] == '\0'; +} + + +static int read_yn(const char *line, void *arg) +{ + char *dest = arg; + int retval = 1; + + if (!strcasecmp("yes", line)) + *dest = 1; + else if (!strcasecmp("no", line)) + *dest = 0; + else retval = 0; + + return retval; +} + + +/* read a dhcp option and add it to opt_list */ +static int read_opt(const char *const_line, void *arg) +{ + struct option_set **opt_list = arg; + char *opt, *val, *endptr; + struct dhcp_option *option; + int retval = 0, length; + char buffer[8]; + char *line; + 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; + if (!(opt = strtok(line, " \t="))) return 0; + + for (option = dhcp_options; option->code; option++) + if (!strcasecmp(option->name, opt)) + break; + + if (!option->code) return 0; + + do { + if (!(val = strtok(NULL, ", \t"))) break; + length = 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; + 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; + opt = val; + retval = 1; + } + break; + case OPTION_BOOLEAN: + retval = read_yn(val, buffer); + break; + case OPTION_U8: + buffer[0] = strtoul(val, &endptr, 0); + retval = (endptr[0] == '\0'); + break; + case OPTION_U16: + *result_u16 = htons(strtoul(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_S16: + *result_u16 = htons(strtol(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_U32: + *result_u32 = htonl(strtoul(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_S32: + *result_u32 = htonl(strtol(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + 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; + uint8_t *mac_bytes; + uint32_t *ip; + + + /* Allocate memory for addresses */ + mac_bytes = xmalloc(sizeof(unsigned char) * 8); + ip = xmalloc(sizeof(uint32_t)); + + /* Read mac */ + line = (char *) const_line; + mac_string = strtok(line, " \t"); + read_mac(mac_string, mac_bytes); + + /* Read ip */ + ip_string = strtok(NULL, " \t"); + read_ip(ip_string, ip); + + addStaticLease(arg, mac_bytes, ip); + +#ifdef UDHCP_DEBUG + printStaticLeases(arg); +#endif + + return 1; + +} + + +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), ""}, + {"static_lease",read_staticlease, &(server_config.static_leases), ""}, + /*ADDME: static lease */ + {"", NULL, NULL, ""} +}; + + +int read_config(const char *file) +{ + FILE *in; + char buffer[READ_CONFIG_BUF_SIZE], *token, *line; +#ifdef UDHCP_DEBUG + char orig[READ_CONFIG_BUF_SIZE]; +#endif + int i, lm = 0; + + for (i = 0; keywords[i].keyword[0]; i++) + if (keywords[i].def[0]) + 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)) { + lm++; + if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0'; +#ifdef UDHCP_DEBUG + strcpy(orig, buffer); +#endif + if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0'; + + if (!(token = strtok(buffer, " \t"))) continue; + if (!(line = strtok(NULL, ""))) continue; + + /* eat leading whitespace */ + line = line + strspn(line, " \t="); + /* eat trailing whitespace */ + for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--); + line[i] = '\0'; + + 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, "Failure parsing line %d of %s", lm, file); + DEBUG(LOG_ERR, "unable to parse '%s'", orig); + /* reset back to the default value */ + keywords[i].handler(keywords[i].def, keywords[i].var); + } + } + fclose(in); + return 1; +} + + +void write_leases(void) +{ + FILE *fp; + unsigned int i; + char buf[255]; + time_t curr = time(0); + unsigned long tmp_time; + + if (!(fp = fopen(server_config.lease_file, "w"))) { + LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file); + return; + } + + for (i = 0; i < server_config.max_leases; i++) { + if (leases[i].yiaddr != 0) { + + /* screw with the time in the struct, for easier writing */ + tmp_time = leases[i].expires; + + if (server_config.remaining) { + if (lease_expired(&(leases[i]))) + leases[i].expires = 0; + else leases[i].expires -= curr; + } /* else stick with the time we got */ + leases[i].expires = htonl(leases[i].expires); + fwrite(&leases[i], sizeof(struct dhcpOfferedAddr), 1, fp); + + /* Then restore it when done. */ + leases[i].expires = tmp_time; + } + } + fclose(fp); + + if (server_config.notify_file) { + sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file); + system(buf); + } +} + + +void 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); + 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); + break; + } + i++; + } + } + DEBUG(LOG_INFO, "Read %d leases", i); + fclose(fp); +} diff --git a/busybox/networking/udhcp/files.h b/busybox/networking/udhcp/files.h new file mode 100644 index 0000000..279738a --- /dev/null +++ b/busybox/networking/udhcp/files.h @@ -0,0 +1,17 @@ +/* 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/busybox/networking/udhcp/frontend.c b/busybox/networking/udhcp/frontend.c new file mode 100644 index 0000000..fa77ab9 --- /dev/null +++ b/busybox/networking/udhcp/frontend.c @@ -0,0 +1,16 @@ +#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/busybox/networking/udhcp/leases.c b/busybox/networking/udhcp/leases.c new file mode 100644 index 0000000..4da21a2 --- /dev/null +++ b/busybox/networking/udhcp/leases.c @@ -0,0 +1,158 @@ +/* + * leases.c -- tools to manage DHCP leases + * Russ Dill <Russ.Dill@asu.edu> July 2001 + */ + +#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 "leases.h" +#include "arpping.h" +#include "common.h" + +#include "static_leases.h" + + +uint8_t blank_chaddr[] = {[0 ... 15] = 0}; + +/* clear every lease out that chaddr OR yiaddr matches and is nonzero */ +void clear_lease(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)); + } +} + + +/* add a lease into the table, clearing out any old ones */ +struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease) +{ + struct dhcpOfferedAddr *oldest; + + /* clean out any old ones */ + clear_lease(chaddr, yiaddr); + + oldest = oldest_expired_lease(); + + if (oldest) { + memcpy(oldest->chaddr, chaddr, 16); + oldest->yiaddr = yiaddr; + oldest->expires = time(0) + lease; + } + + return oldest; +} + + +/* true if a lease has expired */ +int 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; + +} + + +/* Find the first lease that matches chaddr, NULL if no match */ +struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_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(uint32_t yiaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_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(uint32_t addr) +{ + struct in_addr temp; + + 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; +} + + +/* 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... */ +uint32_t find_address(int check_expired) +{ + uint32_t addr, ret; + struct dhcpOfferedAddr *lease = NULL; + + addr = ntohl(server_config.start); /* addr is in host order here */ + for (;addr <= ntohl(server_config.end); addr++) { + + /* ie, 192.168.55.0 */ + if (!(addr & 0xFF)) continue; + + /* ie, 192.168.55.255 */ + if ((addr & 0xFF) == 0xFF) continue; + + /* Only do if it isn't an assigned as a static lease */ + if(!reservedIp(server_config.static_leases, htonl(addr))) + { + + /* 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; + } + } + } + return 0; +} diff --git a/busybox/networking/udhcp/leases.h b/busybox/networking/udhcp/leases.h new file mode 100644 index 0000000..b13fa72 --- /dev/null +++ b/busybox/networking/udhcp/leases.h @@ -0,0 +1,23 @@ +/* leases.h */ +#ifndef _LEASES_H +#define _LEASES_H + + +struct dhcpOfferedAddr { + uint8_t chaddr[16]; + uint32_t yiaddr; /* network order */ + uint32_t expires; /* host order */ +}; + +extern uint8_t blank_chaddr[]; + +void clear_lease(uint8_t *chaddr, uint32_t yiaddr); +struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease); +int lease_expired(struct dhcpOfferedAddr *lease); +struct dhcpOfferedAddr *oldest_expired_lease(void); +struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr); +struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr); +uint32_t find_address(int check_expired); + + +#endif diff --git a/busybox/networking/udhcp/libbb_udhcp.h b/busybox/networking/udhcp/libbb_udhcp.h new file mode 100644 index 0000000..51e1571 --- /dev/null +++ b/busybox/networking/udhcp/libbb_udhcp.h @@ -0,0 +1,54 @@ +/* libbb_udhcp.h - busybox compatability wrapper */ + +/* bit of a hack, do this no matter what the order of the includes. + * (for busybox) */ + +#ifdef CONFIG_INSTALL_NO_USR +#undef DEFAULT_SCRIPT +#define DEFAULT_SCRIPT "/share/udhcpc/default.script" +#endif + +#ifndef _LIBBB_UDHCP_H +#define _LIBBB_UDHCP_H + +#ifdef IN_BUSYBOX +#include "busybox.h" + +#ifdef CONFIG_FEATURE_UDHCP_SYSLOG +#define UDHCP_SYSLOG +#endif + +#ifdef CONFIG_FEATURE_UDHCP_DEBUG +#define UDHCP_DEBUG +#endif + +#define COMBINED_BINARY +#include "version.h" + +#define xfopen bb_xfopen + +#else /* ! BB_VER */ + +#include <stdlib.h> +#include <stdio.h> +#include <sys/sysinfo.h> + +#define TRUE 1 +#define FALSE 0 + +#define xmalloc malloc +#define xcalloc calloc + +static inline FILE *xfopen(const char *file, const char *mode) +{ + FILE *fp; + if (!(fp = fopen(file, mode))) { + perror("could not open input file"); + exit(0); + } + return fp; +} + +#endif /* BB_VER */ + +#endif /* _LIBBB_UDHCP_H */ diff --git a/busybox/networking/udhcp/options.c b/busybox/networking/udhcp/options.c new file mode 100644 index 0000000..d75bc5a --- /dev/null +++ b/busybox/networking/udhcp/options.c @@ -0,0 +1,228 @@ +/* + * options.c -- DHCP server option packet tools + * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 + */ + +#include <stdlib.h> +#include <string.h> + +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "common.h" + + +/* supported options are easily added here */ +struct dhcp_option dhcp_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} +}; + +/* 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 +}; + + +/* get an option with bounds checking (warning, not aligned). */ +uint8_t *get_option(struct dhcpMessage *packet, int code) +{ + int i, length; + uint8_t *optionptr; + int over = 0, done = 0, curr = OPTION_FIELD; + + optionptr = packet->options; + i = 0; + length = 308; + while (!done) { + if (i >= length) { + LOG(LOG_WARNING, "bogus packet, option fields too long."); + 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) { + optionptr = packet->file; + i = 0; + length = 128; + curr = FILE_FIELD; + } else if (curr == FILE_FIELD && over & SNAME_FIELD) { + optionptr = packet->sname; + i = 0; + length = 64; + curr = SNAME_FIELD; + } else done = 1; + break; + default: + i += optionptr[OPT_LEN + i] + 2; + } + } + return NULL; +} + + +/* return the position of the 'end' option (no bounds checking) */ +int 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; + } + return i; +} + + +/* add an option string to the options (an option string contains an option code, + * length, then data) */ +int 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]); + return 0; + } + DEBUG(LOG_INFO, "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; +} + + +/* add a one to four byte option to a packet */ +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) +{ + char length = 0; + int i; + uint8_t option[2 + 4]; + uint8_t *u8; + uint16_t *u16; + uint32_t *u32; + uint32_t aligned; + u8 = (uint8_t *) &aligned; + u16 = (uint16_t *) &aligned; + u32 = &aligned; + + for (i = 0; dhcp_options[i].code; i++) + if (dhcp_options[i].code == code) { + length = option_lengths[dhcp_options[i].flags & TYPE_MASK]; + } + + 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; + } +} diff --git a/busybox/networking/udhcp/options.h b/busybox/networking/udhcp/options.h new file mode 100644 index 0000000..fbe54c8 --- /dev/null +++ b/busybox/networking/udhcp/options.h @@ -0,0 +1,40 @@ +/* options.h */ +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#include "packet.h" + +#define TYPE_MASK 0x0F + +enum { + OPTION_IP=1, + OPTION_IP_PAIR, + OPTION_STRING, + OPTION_BOOLEAN, + OPTION_U8, + OPTION_U16, + OPTION_S16, + OPTION_U32, + 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 */ + +struct dhcp_option { + char name[10]; + char flags; + uint8_t code; +}; + +extern struct dhcp_option dhcp_options[]; +extern int option_lengths[]; + +uint8_t *get_option(struct dhcpMessage *packet, int code); +int end_option(uint8_t *optionptr); +int add_option_string(uint8_t *optionptr, uint8_t *string); +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_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); + +#endif diff --git a/busybox/networking/udhcp/packet.c b/busybox/networking/udhcp/packet.c new file mode 100644 index 0000000..1c6bb0c --- /dev/null +++ b/busybox/networking/udhcp/packet.c @@ -0,0 +1,202 @@ +#include <unistd.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <features.h> +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif +#include <errno.h> + +#include "packet.h" +#include "dhcpd.h" +#include "options.h" +#include "common.h" + + +void init_header(struct dhcpMessage *packet, char type) +{ + memset(packet, 0, sizeof(struct dhcpMessage)); + switch (type) { + case DHCPDISCOVER: + case DHCPREQUEST: + case DHCPRELEASE: + case DHCPINFORM: + packet->op = BOOTREQUEST; + break; + case DHCPOFFER: + case DHCPACK: + case DHCPNAK: + packet->op = BOOTREPLY; + } + packet->htype = ETH_10MB; + packet->hlen = ETH_10MB_LEN; + packet->cookie = htonl(DHCP_MAGIC); + packet->options[0] = DHCP_END; + add_simple_option(packet->options, DHCP_MESSAGE_TYPE, 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 bytes; + int i; + const char broken_vendors[][8] = { + "MSFT 98", + "" + }; + char unsigned *vendor; + + memset(packet, 0, sizeof(struct dhcpMessage)); + bytes = read(fd, packet, sizeof(struct dhcpMessage)); + if (bytes < 0) { + DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); + return -1; + } + + if (ntohl(packet->cookie) != DHCP_MAGIC) { + LOG(LOG_ERR, "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] == (uint8_t) 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); + } + } + } + + + return bytes; +} + + +uint16_t checksum(void *addr, int count) +{ + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register int32_t sum = 0; + uint16_t *source = (uint16_t *) addr; + + while (count > 1) { + /* This is the inner loop */ + sum += *source++; + count -= 2; + } + + /* Add left-over byte, if any */ + if (count > 0) { + /* Make sure that the left-over byte is added correctly both + * with little and big endian hosts */ + uint16_t tmp = 0; + *(uint8_t *) (&tmp) = * (uint8_t *) source; + sum += tmp; + } + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} + + +/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */ +int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex) +{ + int fd; + int result; + struct sockaddr_ll dest; + struct udp_dhcp_packet packet; + + if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + DEBUG(LOG_ERR, "socket call failed: %m"); + return -1; + } + + memset(&dest, 0, sizeof(dest)); + memset(&packet, 0, sizeof(packet)); + + 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; + } + + 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 */ + 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.ip.ihl = sizeof(packet.ip) >> 2; + packet.ip.version = IPVERSION; + packet.ip.ttl = IPDEFTTL; + packet.ip.check = 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"); + } + close(fd); + return result; +} + + +/* Let the kernel do all the work for packet generation */ +int 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; + + 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; + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(dest_port); + client.sin_addr.s_addr = dest_ip; + + if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) + return -1; + + result = write(fd, payload, sizeof(struct dhcpMessage)); + close(fd); + return result; +} diff --git a/busybox/networking/udhcp/packet.h b/busybox/networking/udhcp/packet.h new file mode 100644 index 0000000..f5859e8 --- /dev/null +++ b/busybox/networking/udhcp/packet.h @@ -0,0 +1,41 @@ +#ifndef _PACKET_H +#define _PACKET_H + +#include <netinet/udp.h> +#include <netinet/ip.h> + +struct dhcpMessage { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t cookie; + uint8_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); +uint16_t checksum(void *addr, int count); +int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex); +int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port); + + +#endif diff --git a/busybox/networking/udhcp/pidfile.c b/busybox/networking/udhcp/pidfile.c new file mode 100644 index 0000000..2e0c753 --- /dev/null +++ b/busybox/networking/udhcp/pidfile.c @@ -0,0 +1,75 @@ +/* pidfile.c + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill <Russ.Dill@asu.edu> September 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 <stdio.h> +#include <stdlib.h> + +#include "pidfile.h" +#include "common.h" + +static char *saved_pidfile; + +static void pidfile_delete(void) +{ + if (saved_pidfile) unlink(saved_pidfile); +} + + +int pidfile_acquire(const char *pidfile) +{ + int pid_fd; + if (!pidfile) return -1; + + pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); + if (pid_fd < 0) { + LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile); + } else { + lockf(pid_fd, F_LOCK, 0); + if (!saved_pidfile) + atexit(pidfile_delete); + saved_pidfile = (char *) pidfile; + } + + 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); +} + + + + diff --git a/busybox/networking/udhcp/pidfile.h b/busybox/networking/udhcp/pidfile.h new file mode 100644 index 0000000..ea97d1d --- /dev/null +++ b/busybox/networking/udhcp/pidfile.h @@ -0,0 +1,25 @@ +/* pidfile.h + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill <Russ.Dill@asu.edu> September 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(const char *pidfile); +void pidfile_write_release(int pid_fd); + diff --git a/busybox/networking/udhcp/script.c b/busybox/networking/udhcp/script.c new file mode 100644 index 0000000..820fbb0 --- /dev/null +++ b/busybox/networking/udhcp/script.c @@ -0,0 +1,233 @@ +/* script.c + * + * 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. + */ + +#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 "dhcpc.h" +#include "common.h" + +/* get a rough idea of how long an option will be (rounding up...) */ +static const int max_option_length[] = { + [OPTION_IP] = sizeof("255.255.255.255 "), + [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, + [OPTION_STRING] = 1, + [OPTION_BOOLEAN] = sizeof("yes "), + [OPTION_U8] = sizeof("255 "), + [OPTION_U16] = sizeof("65535 "), + [OPTION_S16] = sizeof("-32768 "), + [OPTION_U32] = sizeof("4294967295 "), + [OPTION_S32] = sizeof("-2147483684 "), +}; + + +static inline int upper_length(int length, int opt_index) +{ + return max_option_length[opt_index] * + (length / option_lengths[opt_index]); +} + + +static int sprintip(char *dest, char *pre, uint8_t *ip) +{ + 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) +{ + 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; +} + + +/* Fill dest with the text of option 'option'. */ +static void fill_options(char *dest, uint8_t *option, struct dhcp_option *type_p) +{ + int type, optlen; + uint16_t val_u16; + int16_t val_s16; + uint32_t val_u32; + int32_t val_s32; + int len = option[OPT_LEN - 2]; + + dest += sprintf(dest, "%s=", type_p->name); + + type = type_p->flags & TYPE_MASK; + optlen = option_lengths[type]; + for(;;) { + switch (type) { + case OPTION_IP_PAIR: + dest += sprintip(dest, "", option); + *(dest++) = '/'; + option += 4; + optlen = 4; + case OPTION_IP: /* Works regardless of host byte order. */ + dest += sprintip(dest, "", option); + break; + case OPTION_BOOLEAN: + dest += sprintf(dest, *option ? "yes" : "no"); + break; + case OPTION_U8: + dest += sprintf(dest, "%u", *option); + break; + case OPTION_U16: + memcpy(&val_u16, option, 2); + dest += sprintf(dest, "%u", ntohs(val_u16)); + break; + case OPTION_S16: + memcpy(&val_s16, option, 2); + dest += sprintf(dest, "%d", ntohs(val_s16)); + break; + case OPTION_U32: + memcpy(&val_u32, option, 4); + dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32)); + break; + case OPTION_S32: + memcpy(&val_s32, option, 4); + dest += sprintf(dest, "%ld", (long) ntohl(val_s32)); + break; + case OPTION_STRING: + memcpy(dest, option, len); + dest[len] = '\0'; + return; /* Short circuit this case */ + } + option += optlen; + len -= optlen; + if (len <= 0) break; + dest += sprintf(dest, " "); + } +} + + +/* put all the parameters into an environment */ +static char **fill_envp(struct dhcpMessage *packet) +{ + int num_options = 0; + int i, j; + char **envp; + uint8_t *temp; + struct in_addr subnet; + char over = 0; + + if (packet == NULL) + num_options = 0; + else { + for (i = 0; dhcp_options[i].code; i++) + if (get_option(packet, dhcp_options[i].code)) { + num_options++; + if (dhcp_options[i].code == DHCP_SUBNET) + num_options++; /* for mton */ + } + if (packet->siaddr) num_options++; + if ((temp = get_option(packet, DHCP_OPTION_OVER))) + over = *temp; + if (!(over & FILE_FIELD) && packet->file[0]) num_options++; + if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; + } + + envp = xcalloc(sizeof(char *), num_options + 5); + j = 0; + asprintf(&envp[j++], "interface=%s", client_config.interface); + asprintf(&envp[j++], "%s=%s", "PATH", + getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin"); + asprintf(&envp[j++], "%s=%s", "HOME", getenv("HOME") ? : "/"); + + if (packet == NULL) return envp; + + envp[j] = xmalloc(sizeof("ip=255.255.255.255")); + sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr); + + + for (i = 0; dhcp_options[i].code; i++) { + if (!(temp = get_option(packet, dhcp_options[i].code))) + continue; + envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], + dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2); + fill_options(envp[j++], temp, &dhcp_options[i]); + + /* Fill in a subnet bits option for things like /24 */ + if (dhcp_options[i].code == DHCP_SUBNET) { + memcpy(&subnet, temp, 4); + asprintf(&envp[j++], "mask=%d", mton(&subnet)); + } + } + if (packet->siaddr) { + envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); + sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr); + } + 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); + } + 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); + } + return envp; +} + + +/* Call a script with a par file and env vars */ +void run_script(struct dhcpMessage *packet, const char *name) +{ + int pid; + char **envp, **curr; + + if (client_config.script == NULL) + return; + + DEBUG(LOG_INFO, "vforking and execle'ing %s", client_config.script); + + envp = fill_envp(packet); + /* call script */ + pid = vfork(); + if (pid) { + waitpid(pid, NULL, 0); + for (curr = envp; *curr; curr++) free(*curr); + free(envp); + return; + } else if (pid == 0) { + /* close fd's? */ + + /* exec script */ + execle(client_config.script, client_config.script, + name, NULL, envp); + LOG(LOG_ERR, "script %s failed: %m", client_config.script); + exit(1); + } +} diff --git a/busybox/networking/udhcp/script.h b/busybox/networking/udhcp/script.h new file mode 100644 index 0000000..87a20cc --- /dev/null +++ b/busybox/networking/udhcp/script.h @@ -0,0 +1,6 @@ +#ifndef _SCRIPT_H +#define _SCRIPT_H + +void run_script(struct dhcpMessage *packet, const char *name); + +#endif diff --git a/busybox/networking/udhcp/serverpacket.c b/busybox/networking/udhcp/serverpacket.c new file mode 100644 index 0000000..75d55bd --- /dev/null +++ b/busybox/networking/udhcp/serverpacket.c @@ -0,0 +1,275 @@ +/* serverpacket.c + * + * Construct and send DHCP server packets + * + * 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 <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <time.h> + +#include "serverpacket.h" +#include "dhcpd.h" +#include "options.h" +#include "common.h" +#include "static_leases.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"); + + return kernel_packet(payload, server_config.server, SERVER_PORT, + payload->giaddr, SERVER_PORT); +} + + +/* 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) +{ + uint8_t *chaddr; + uint32_t ciaddr; + + if (force_broadcast) { + DEBUG(LOG_INFO, "broadcasting packet to client (NAK)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else if (payload->ciaddr) { + DEBUG(LOG_INFO, "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)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else { + DEBUG(LOG_INFO, "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); +} + + +/* 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; +} + + +static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) +{ + init_header(packet, type); + packet->xid = oldpacket->xid; + memcpy(packet->chaddr, oldpacket->chaddr, 16); + packet->flags = oldpacket->flags; + packet->giaddr = oldpacket->giaddr; + packet->ciaddr = oldpacket->ciaddr; + add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); +} + + +/* add in the bootp options */ +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); + if (server_config.boot_file) + strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1); +} + + +/* send a DHCP OFFER to a DHCP DISCOVER */ +int sendOffer(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + struct dhcpOfferedAddr *lease = NULL; + uint32_t req_align, lease_time_align = server_config.lease; + uint8_t *req, *lease_time; + struct option_set *curr; + struct in_addr addr; + + uint32_t static_lease_ip; + + init_packet(&packet, oldpacket, DHCPOFFER); + + static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr); + + /* ADDME: if static, short circuit */ + if(!static_lease_ip) + { + /* 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) && + + !static_lease_ip && /* Check that its not a static lease */ + /* and is not already taken/offered */ + ((!(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 */ + } else { + /* Is it a static lease? (No, because find_address skips static lease) */ + 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; + } + + /* 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 */ + else + { + /* It is a static lease... use it */ + packet.yiaddr = static_lease_ip; + } + + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr)); + return send_packet(&packet, 0); +} + + +int sendNAK(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + + init_packet(&packet, oldpacket, DHCPNAK); + + DEBUG(LOG_INFO, "sending NAK"); + return send_packet(&packet, 1); +} + + +int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr) +{ + struct dhcpMessage packet; + struct option_set *curr; + uint8_t *lease_time; + uint32_t lease_time_align = server_config.lease; + struct in_addr addr; + + 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; + } + + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr)); + + if (send_packet(&packet, 0) < 0) + return -1; + + add_lease(packet.chaddr, packet.yiaddr, lease_time_align); + + return 0; +} + + +int 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) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + return send_packet(&packet, 0); +} diff --git a/busybox/networking/udhcp/serverpacket.h b/busybox/networking/udhcp/serverpacket.h new file mode 100644 index 0000000..9024928 --- /dev/null +++ b/busybox/networking/udhcp/serverpacket.h @@ -0,0 +1,12 @@ +#ifndef _SERVERPACKET_H +#define _SERVERPACKET_H + +#include "packet.h" + +int sendOffer(struct dhcpMessage *oldpacket); +int sendNAK(struct dhcpMessage *oldpacket); +int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr); +int send_inform(struct dhcpMessage *oldpacket); + + +#endif diff --git a/busybox/networking/udhcp/signalpipe.c b/busybox/networking/udhcp/signalpipe.c new file mode 100644 index 0000000..074c8a6 --- /dev/null +++ b/busybox/networking/udhcp/signalpipe.c @@ -0,0 +1,78 @@ +/* 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 <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> + + +#include "signalpipe.h" +#include "common.h" + +static int signal_pipe[2]; + +static void signal_handler(int sig) +{ + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) + DEBUG(LOG_ERR, "Could not send signal: %m"); +} + + +/* Call this before doing anything else. Sets up the socket pair + * and installs the signal handler */ +void udhcp_sp_setup(void) +{ + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); + signal(SIGUSR1, signal_handler); + signal(SIGUSR2, signal_handler); + signal(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 udhcp_sp_fd_set(fd_set *rfds, int extra_fd) +{ + FD_ZERO(rfds); + FD_SET(signal_pipe[0], rfds); + if (extra_fd >= 0) FD_SET(extra_fd, rfds); + return signal_pipe[0] > extra_fd ? signal_pipe[0] : 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 udhcp_sp_read(fd_set *rfds) +{ + int sig; + + if (!FD_ISSET(signal_pipe[0], rfds)) + return 0; + + if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) + return -1; + + return sig; +} diff --git a/busybox/networking/udhcp/signalpipe.h b/busybox/networking/udhcp/signalpipe.h new file mode 100644 index 0000000..70c1e57 --- /dev/null +++ b/busybox/networking/udhcp/signalpipe.h @@ -0,0 +1,22 @@ +/* signalpipe.h + * + * 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. + */ + +void udhcp_sp_setup(void); +int udhcp_sp_fd_set(fd_set *rfds, int extra_fd); +int udhcp_sp_read(fd_set *rfds); diff --git a/busybox/networking/udhcp/socket.c b/busybox/networking/udhcp/socket.c new file mode 100644 index 0000000..7b05752 --- /dev/null +++ b/busybox/networking/udhcp/socket.c @@ -0,0 +1,132 @@ +/* + * socket.c -- DHCP server client/server socket creation + * + * udhcp client/server + * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> + * Chris Trew <ctrew@moreton.com.au> + * + * 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. + */ + +#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 +#include <netpacket/packet.h> +#include <net/ethernet.h> +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#endif + +#include "socket.h" +#include "common.h" + +int read_interface(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); + + 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; + } + } + + 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"); + 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"); + return -1; + } + } else { + LOG(LOG_ERR, "socket failed!: %m"); + return -1; + } + close(fd); + return 0; +} + + +int listen_socket(uint32_t ip, int port, 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", 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; +} diff --git a/busybox/networking/udhcp/socket.h b/busybox/networking/udhcp/socket.h new file mode 100644 index 0000000..66179d4 --- /dev/null +++ b/busybox/networking/udhcp/socket.h @@ -0,0 +1,8 @@ +/* socket.h */ +#ifndef _SOCKET_H +#define _SOCKET_H + +int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp); +int listen_socket(uint32_t ip, int port, char *inf); + +#endif diff --git a/busybox/networking/udhcp/static_leases.c b/busybox/networking/udhcp/static_leases.c new file mode 100644 index 0000000..1124d39 --- /dev/null +++ b/busybox/networking/udhcp/static_leases.c @@ -0,0 +1,119 @@ +/* + * static_leases.c -- Couple of functions to assist with storing and + * retrieving data for static leases + * + * Wade Berrier <wberrier@myrealbox.com> September 2004 + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "static_leases.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 */ +int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip) +{ + + struct static_lease *cur; + struct static_lease *new_static_lease; + + /* Build new node */ + new_static_lease = xmalloc(sizeof(struct static_lease)); + new_static_lease->mac = mac; + 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 + { + cur = *lease_struct; + while(cur->next != NULL) + { + cur = cur->next; + } + + cur->next = new_static_lease; + } + + return 1; + +} + +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg) +{ + uint32_t return_ip; + struct static_lease *cur = lease_struct; + uint8_t *mac = arg; + + return_ip = 0; + + while(cur != NULL) + { + /* If the client has the correct mac */ + if(memcmp(cur->mac, mac, 6) == 0) + { + return_ip = *(cur->ip); + } + + cur = cur->next; + } + + return return_ip; + +} + +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip) +{ + struct static_lease *cur = lease_struct; + + uint32_t return_val = 0; + + while(cur != NULL) + { + /* If the client has the correct ip */ + if(*cur->ip == ip) + return_val = 1; + + cur = cur->next; + } + + return return_val; + +} + +#ifdef 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 printStaticLeases(struct static_lease **arg) +{ + /* Get a pointer to the linked list */ + struct static_lease *cur = *arg; + + while(cur != NULL) + { + /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */ + printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac)); + /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */ + printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip)); + + cur = cur->next; + } + + +} +#endif + + + diff --git a/busybox/networking/udhcp/static_leases.h b/busybox/networking/udhcp/static_leases.h new file mode 100644 index 0000000..d06520b --- /dev/null +++ b/busybox/networking/udhcp/static_leases.h @@ -0,0 +1,25 @@ +/* static_leases.h */ +#ifndef _STATIC_LEASES_H +#define _STATIC_LEASES_H + +#include "dhcpd.h" + +/* Config file will pass static lease info to this function which will add it + * to a data structure that can be searched later */ +int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip); + +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg); + +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip); + +#ifdef UDHCP_DEBUG +/* Print out static leases just to check what's going on */ +void printStaticLeases(struct static_lease **lease_struct); +#endif + +#endif + + + diff --git a/busybox/networking/udhcp/version.h b/busybox/networking/udhcp/version.h new file mode 100644 index 0000000..3862539 --- /dev/null +++ b/busybox/networking/udhcp/version.h @@ -0,0 +1,6 @@ +#ifndef _UDHCP_VERSION_H +#define _UDHCP_VERSION_H + +#define VERSION "0.9.9-pre" + +#endif diff --git a/busybox/networking/vconfig.c b/busybox/networking/vconfig.c new file mode 100644 index 0000000..bbd2987 --- /dev/null +++ b/busybox/networking/vconfig.c @@ -0,0 +1,184 @@ +/* vi: set sw=4 ts=4: */ +/* + * vconfig implementation for busybox + * + * 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 + * + */ + +/* BB_AUDIT SUSv3 N/A */ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <net/if.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 */ + +/* 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])) { + bb_show_usage(); + } + } + return table - 1; +} + +static const char cmds[] = { + 4, ADD_VLAN_CMD, 7, + 'a', 'd', 'd', 0, + 3, DEL_VLAN_CMD, 7, + 'r', 'e', 'm', 0, + 3, SET_VLAN_NAME_TYPE_CMD, 17, + 's', 'e', 't', '_', + 'n', 'a', 'm', 'e', '_', + 't', 'y', 'p', 'e', 0, + 4, SET_VLAN_FLAG_CMD, 12, + 's', 'e', 't', '_', + 'f', 'l', 'a', 'g', 0, + 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18, + 's', 'e', 't', '_', + 'e', 'g', 'r', 'e', 's', 's', '_', + 'm', 'a', 'p', 0, + 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16, + 's', 'e', 't', '_', + 'i', 'n', 'g', 'r', 'e', 's', 's', '_', + 'm', 'a', 'p', 0, +}; + +static const char name_types[] = { + 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', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + '_', 'N', 'O', '_', 'P', 'A', 'D', 0, + VLAN_NAME_TYPE_RAW_PLUS_VID, 15, + 'D', 'E', 'V', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + 0, + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20, + 'D', 'E', 'V', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + '_', 'N', 'O', '_', 'P', 'A', 'D', 0, +}; + +static const char conf_file_name[] = "/proc/net/vlan/config"; + +int vconfig_main(int argc, char **argv) +{ + struct vlan_ioctl_args ifr; + const char *p; + int fd; + + if (argc < 3) { + bb_show_usage(); + } + + /* 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); + } + + memset(&ifr, 0, sizeof(struct vlan_ioctl_args)); + + ++argv; + p = xfind_str(cmds+2, *argv); + ifr.cmd = *p; + if (argc != p[-1]) { + bb_show_usage(); + } + + 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]); + p = argv[2]; + + /* I suppose one could try to combine some of the function calls below, + * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized + * (unsigned) int members of a unions. But because of the range checking, + * 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); + } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */ + ifr.u.VID = bb_xgetularg10_bnd(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); + } + } + + 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); + } + + return 0; +} + diff --git a/busybox/networking/wget.c b/busybox/networking/wget.c new file mode 100644 index 0000000..45acef4 --- /dev/null +++ b/busybox/networking/wget.c @@ -0,0 +1,868 @@ +/* vi: set sw=4 ts=4: */ +/* + * wget - retrieve a file using HTTP or FTP + * + * Chip Rosenthal Covad Communications <chip@laserlink.net> + * + */ + +#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" + +struct host_info { + char *host; + int port; + char *path; + int is_ftp; + char *user; +}; + +static void parse_url(char *url, struct host_info *h); +static FILE *open_socket(struct sockaddr_in *s_in); +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; +#endif + +static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue) +{ + if (output != stdout && do_continue==0) { + fclose(output); + unlink(fname_out); + } +} + +/* 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) +{ + size_t ret = 0; + + do { + clearerr(stream); + ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream); + } while (ret < nmemb && ferror(stream) && errno == EINTR); + + return ret; +} + +/* 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) +{ + size_t ret = 0; + + do { + clearerr(stream); + ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream); + } while (ret < nmemb && ferror(stream) && errno == EINTR); + + return ret; +} + +/* 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) +{ + char *ret; + + do { + clearerr(stream); + 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); } + + +#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(unsigned 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; +} +#endif + +#define WGET_OPT_CONTINUE 1 +#define WGET_OPT_QUIET 2 +#define WGET_OPT_PASSIVE 4 +#define WGET_OPT_OUTNAME 8 +#define WGET_OPT_HEADER 16 +#define WGET_OPT_PREFIX 32 +#define WGET_OPT_PROXY 64 + +static const struct option wget_long_options[] = { + { "continue", 0, NULL, 'c' }, + { "quiet", 0, NULL, 'q' }, + { "passive-ftp", 0, NULL, 139 }, + { "output-document", 1, NULL, 'O' }, + { "header", 1, NULL, 131 }, + { "directory-prefix",1, NULL, 'P' }, + { "proxy", 1, NULL, 'Y' }, + { 0, 0, 0, 0 } +}; + +int wget_main(int argc, char **argv) +{ + int n, try=5, status; + unsigned long opt; + int port; + 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); + struct host_info server, target; + struct sockaddr_in s_in; + llist_t *headers_llist = NULL; + + 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 use_proxy = 1; /* Use proxies if env vars are set */ + char *proxy_flag = "on"; /* Use proxies if env vars are set */ + + /* + * Crack command line. + */ + bb_opt_complementaly = "\203*"; + bb_applet_long_options = wget_long_options; + opt = bb_getopt_ulflags(argc, argv, "cq\213O:\203:P:Y:", &fname_out, &headers_llist, &dir_prefix, &proxy_flag); + if (opt & WGET_OPT_CONTINUE) { + ++do_continue; + } + if (opt & WGET_OPT_QUIET) { + quiet_flag = TRUE; + } + if (strcmp(proxy_flag, "off") == 0) { + /* Use the proxy if necessary. */ + use_proxy = 0; + } + if (opt & WGET_OPT_HEADER) { + while (headers_llist) { + int arglen = strlen(headers_llist->data); + 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, headers_llist->data); + extra_headers_ptr += arglen; + extra_headers_left -= ( arglen + 2 ); + *extra_headers_ptr++ = '\r'; + *extra_headers_ptr++ = '\n'; + *(extra_headers_ptr + 1) = 0; + headers_llist = headers_llist->link; + } + } + if (argc - optind != 1) + bb_show_usage(); + + parse_url(argv[optind], &target); + server.host = target.host; + server.port = target.port; + + /* + * Use the proxy if necessary. + */ + if (use_proxy) { + proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); + if (proxy && *proxy) { + parse_url(bb_xstrdup(proxy), &server); + } else { + use_proxy = 0; + } + } + + /* Guess an output filename */ + if (!fname_out) { + // Dirty hack. Needed because bb_get_last_path_component + // will destroy trailing / by storing '\0' in last byte! + if(target.path[strlen(target.path)-1]!='/') { + 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 (dir_prefix != NULL) + 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 (do_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 (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... */ + bb_lookup_host(&s_in, server.host); + s_in.sin_port = server.port; + if (quiet_flag==FALSE) { + fprintf(stdout, "Connecting to %s[%s]:%d\n", + server.host, inet_ntoa(s_in.sin_addr), ntohs(server.port)); + } + + if (use_proxy || !target.is_ftp) { + /* + * HTTP session + */ + do { + got_clen = chunked = 0; + + if (! --try) + close_delete_and_die("too many redirections"); + + /* + * Open socket to http server + */ + if (sfp) fclose(sfp); + sfp = open_socket(&s_in); + + /* + * Send HTTP request. + */ + if (use_proxy) { + const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n"; +#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL + if (strchr (target.host, ':')) + format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n"; +#endif + fprintf(sfp, format, + target.is_ftp ? "f" : "ht", target.host, + ntohs(target.port), 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); + +#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION + if (target.user) { + fprintf(sfp, "Authorization: Basic %s\r\n", + base64enc(target.user, buf, sizeof(buf))); + } + if (use_proxy && server.user) { + fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", + base64enc(server.user, buf, sizeof(buf))); + } +#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"); + + /* + * 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: + break; + case 206: + if (do_continue) + break; + /*FALLTHRU*/ + default: + chomp(buf); + close_delete_and_die("server returned error %d: %s", atoi(s), buf); + } + + /* + * Retrieve HTTP headers. + */ + while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { + if (strcasecmp(buf, "content-length") == 0) { + unsigned long value; + if (safe_strtoul(s, &value)) { + close_delete_and_die("content-length %s is garbage", s); + } + filesize = value; + 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 (strcasecmp(buf, "location") == 0) { + if (s[0] == '/') + target.path = bb_xstrdup(s+1); + else { + parse_url(bb_xstrdup(s), &target); + if (use_proxy == 0) { + server.host = target.host; + server.port = target.port; + } + bb_lookup_host(&s_in, server.host); + s_in.sin_port = server.port; + break; + } + } + } + } while(status >= 300); + + dfp = sfp; + } + else + { + /* + * FTP session + */ + if (! target.user) + target.user = bb_xstrdup("anonymous:busybox@"); + + sfp = open_socket(&s_in); + if (ftpcmd(NULL, NULL, sfp, buf) != 220) + close_delete_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: + break; + case 331: + if (ftpcmd("PASS ", s, sfp, buf) == 230) + break; + /* FALLTHRU (failed login) */ + default: + close_delete_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) { + unsigned long value; + if (safe_strtoul(buf+4, &value)) { + close_delete_and_die("SIZE value is garbage"); + } + filesize = value; + 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; + s_in.sin_port = htons(port); + dfp = open_socket(&s_in); + + 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("RETR /", target.path, sfp, buf) > 150) + close_delete_and_die("RETR: %s", buf+4); + + } + + + /* + * Retrieve file + */ + if (chunked) { + fgets(buf, sizeof(buf), dfp); + filesize = strtol(buf, (char **) NULL, 16); + } +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(-1); +#endif + do { + while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, ((chunked || got_clen) && (filesize < 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; +#endif + if (got_clen) { + filesize -= n; + } + } + + 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 (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 ((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, *pp; + + 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", "tfp", 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) { + *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; + + pp = h->host; + +#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL + if (h->host[0] == '[') { + char *ep; + + ep = h->host + 1; + while (*ep == ':' || isxdigit (*ep)) + ep++; + if (*ep == ']') { + h->host++; + *ep = '\0'; + pp = ep + 1; + } + } +#endif + + cp = strchr(pp, ':'); + if (cp != NULL) { + *cp++ = '\0'; + h->port = htons(atoi(cp)); + } +} + + +FILE *open_socket(struct sockaddr_in *s_in) +{ + FILE *fp; + + fp = fdopen(xconnect(s_in), "r+"); + if (fp == 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) +{ + 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("fgets()"); + } + buf_ptr = strstr(buf, "\r\n"); + if (buf_ptr) { + *buf_ptr = '\0'; + } + } 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) +{ + int width=0; + get_terminal_width_height(0, &width, NULL); + return (width); +} + +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(STDERR_FILENO, 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); + } +} +#endif + +/* 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. + * + * $Id: wget.c,v 1.75 2004/10/08 08:27:40 andersen Exp $ + */ + + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ |