]> git.mdlowis.com Git - proto/albase.git/commitdiff
Added sdhcp source and rules file
authorMike Lowis <mike.lowis@gentex.com>
Wed, 11 May 2016 16:04:49 +0000 (12:04 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Wed, 11 May 2016 16:04:49 +0000 (12:04 -0400)
12 files changed:
Makefile
source/sdhcp/LICENSE [new file with mode: 0644]
source/sdhcp/Makefile [new file with mode: 0644]
source/sdhcp/Rules.mk [new file with mode: 0644]
source/sdhcp/TODO [new file with mode: 0644]
source/sdhcp/arg.h [new file with mode: 0644]
source/sdhcp/config.mk [new file with mode: 0644]
source/sdhcp/sdhcp.1 [new file with mode: 0644]
source/sdhcp/sdhcp.c [new file with mode: 0644]
source/sdhcp/util.h [new file with mode: 0644]
source/sdhcp/util/eprintf.c [new file with mode: 0644]
source/sdhcp/util/strlcpy.c [new file with mode: 0644]

index 29090dbcd73f58923a1efda18953ba4b732fad06..f7da11f5b6c747633d8770883f9e0d0e217e3088 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,7 @@ include source/sbase/Rules.mk
 include source/sh/Rules.mk
 include source/shadow/Rules.mk
 include source/smdev/Rules.mk
+include source/sdhcp/Rules.mk
 include source/iproute2/Rules.mk
 
 .PHONY: all headers $(PHONY)
diff --git a/source/sdhcp/LICENSE b/source/sdhcp/LICENSE
new file mode 100644 (file)
index 0000000..67aa74f
--- /dev/null
@@ -0,0 +1,23 @@
+MIT/X Consortium License
+
+© 2012 David Galos (galosd83 (at) students.rowan.edu)
+© 2014-2015 Hiltjo Posthuma <hiltjo at codemadness dot org>
+© 2015 Michael Forney <mforney@mforney.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/source/sdhcp/Makefile b/source/sdhcp/Makefile
new file mode 100644 (file)
index 0000000..7126a36
--- /dev/null
@@ -0,0 +1,65 @@
+include config.mk
+
+.POSIX:
+.SUFFIXES: .c .o
+
+HDR = util.h arg.h
+LIB = \
+       util/strlcpy.o \
+       util/eprintf.o
+
+SRC = sdhcp.c
+
+OBJ = $(SRC:.c=.o) $(LIB)
+BIN = $(SRC:.c=)
+MAN = $(SRC:.c=.1)
+
+all: options binlib
+
+options:
+       @echo sdhcp build options:
+       @echo "CFLAGS   = ${CFLAGS}"
+       @echo "LDFLAGS  = ${LDFLAGS}"
+       @echo "CC       = ${CC}"
+
+binlib: util.a
+       $(MAKE) bin
+
+bin: $(BIN)
+
+$(OBJ): $(HDR) config.mk
+
+.o:
+       @echo LD $@
+       @$(LD) -o $@ $< util.a $(LDFLAGS)
+
+.c.o:
+       @echo CC $<
+       @$(CC) -c -o $@ $< $(CFLAGS)
+
+util.a: $(LIB)
+       @echo AR $@
+       @$(AR) -r -c $@ $(LIB)
+       @ranlib $@
+
+install: all
+       @echo installing executables to $(DESTDIR)$(PREFIX)/sbin
+       @mkdir -p $(DESTDIR)$(PREFIX)/sbin
+       @cp -f $(BIN) $(DESTDIR)$(PREFIX)/sbin
+       @cd $(DESTDIR)$(PREFIX)/sbin && chmod 755 $(BIN)
+       @echo installing manual pages to $(DESTDIR)$(MANPREFIX)/man1
+       @mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+       @for m in $(MAN); do sed "s/VERSION/$(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man1/"$$m"; done
+       @cd $(DESTDIR)$(MANPREFIX)/man1 && chmod 644 $(MAN)
+
+uninstall:
+       @echo removing executables from $(DESTDIR)$(PREFIX)/sbin
+       @cd $(DESTDIR)$(PREFIX)/sbin && rm -f $(BIN)
+       @echo removing manual pages from $(DESTDIR)$(MANPREFIX)/man1
+       @cd $(DESTDIR)$(MANPREFIX)/man1 && rm -f $(MAN)
+
+clean:
+       @echo cleaning
+       @rm -f $(BIN) $(OBJ) util.a
+
+.PHONY: all options clean install uninstall
diff --git a/source/sdhcp/Rules.mk b/source/sdhcp/Rules.mk
new file mode 100644 (file)
index 0000000..a536b8a
--- /dev/null
@@ -0,0 +1,20 @@
+PHONY  += sdhcp
+ECLEAN += $(BINDIR)/sdhcp $(SDHCP_OBJS)
+DIRS   += $(SDHCP_OBJDIR)/util
+
+SDHCP_SUBDIR = source/sdhcp
+SDHCP_OBJDIR = $(OBJDIR)/sdhcp
+SDHCP_SRCS   = $(wildcard $(SDHCP_SUBDIR)/*.c $(SDHCP_SUBDIR)/util/*.c)
+SDHCP_OBJS   = $(patsubst $(SDHCP_SUBDIR)/%,$(SDHCP_OBJDIR)/%.o,$(basename $(SDHCP_SRCS)))
+
+SDHCP_INCS   = -I$(BUILDDIR)/include
+SDHCP_DEFS   =
+
+sdhcp: headers $(BINDIR)/sdhcp
+
+$(BINDIR)/sdhcp: $(SDHCP_OBJS)
+       $(CC) -o $@ $^
+
+$(SDHCP_OBJDIR)/%.o: $(SDHCP_SUBDIR)/%.c $(CC)
+       $(CC) $(SDHCP_INCS) $(SDHCP_DEFS) -c -o $@ $<
+
diff --git a/source/sdhcp/TODO b/source/sdhcp/TODO
new file mode 100644 (file)
index 0000000..be4d6cf
--- /dev/null
@@ -0,0 +1,38 @@
+TODO:
+[ ] manual check memcpy bounds.
+[ ] add flag (-s?) to probe a specific DHCP server, not broadcast?
+    probably skip in run() Init: etc stages.
+[ ] replace unsigned char ip[4] and so on from function declarations.
+[?] ipv6 support ?
+
+Changed (for now):
+       - cleanup
+               - code style.
+               - trailing whitespace and use tabs.
+               - remove debug (dbgprintf()) code in sdhcp.c.
+               - code compiles more cleanly (ansi and c99),
+                 -D_BSD_SOURCE added and explicitly added missing headers (time.h and unistd.h).
+               - moved man page from sdhcp.8 to sdhcp.1
+               - changed man page to mandoc.
+       - typos:
+               - sdhcp.c: interface typo.
+               - sdhcp.1: shdcp typo.
+       - replace write() for stdout messages with fprintf()
+       - replace die() with eprintf().
+       - makefile:
+               - man page install should respect $DESTDIR.
+               - make sure on install /sbin and mandir exists.
+               - add config.mk, and follow suckless Makefile style.
+       - add arg.h
+               - first parameter remains interface.
+               - second parameter is optional client-id, used to be hardcoded to
+                 "vaio".
+               - add -d flag, don't update /etc/resolv.conf.
+               - add -i flag, don't set ip.
+               - add -f flag, run in foreground.
+               - add -e flag, run program, this has the following variables set:
+                 $SERVER, DHCP ip.
+                 $DNS, DNS ip.
+                 $ROUTER, router ip.
+                 $MASK, network mask.
+                 $CLIENT, client ip.
diff --git a/source/sdhcp/arg.h b/source/sdhcp/arg.h
new file mode 100644 (file)
index 0000000..4df77a7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN       for (argv0 = *argv, argv++, argc--;\
+                                       argv[0] && argv[0][1]\
+                                       && argv[0][0] == '-';\
+                                       argc--, argv++) {\
+                               char argc_;\
+                               char **argv_;\
+                               int brk_;\
+                               if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+                                       argv++;\
+                                       argc--;\
+                                       break;\
+                               }\
+                               for (brk_ = 0, argv[0]++, argv_ = argv;\
+                                               argv[0][0] && !brk_;\
+                                               argv[0]++) {\
+                                       if (argv_ != argv)\
+                                               break;\
+                                       argc_ = argv[0][0];\
+                                       switch (argc_)
+
+/* Handles obsolete -NUM syntax */
+#define ARGNUM                         case '0':\
+                                       case '1':\
+                                       case '2':\
+                                       case '3':\
+                                       case '4':\
+                                       case '5':\
+                                       case '6':\
+                                       case '7':\
+                                       case '8':\
+                                       case '9'
+
+#define ARGEND                 }\
+                       }
+
+#define ARGC()         argc_
+
+#define ARGNUMF(base)  (brk_ = 1, estrtol(argv[0], (base)))
+
+#define EARGF(x)       ((argv[0][1] == '\0' && argv[1] == NULL)?\
+                               ((x), abort(), (char *)0) :\
+                               (brk_ = 1, (argv[0][1] != '\0')?\
+                                       (&argv[0][1]) :\
+                                       (argc--, argv++, argv[0])))
+
+#define ARGF()         ((argv[0][1] == '\0' && argv[1] == NULL)?\
+                               (char *)0 :\
+                               (brk_ = 1, (argv[0][1] != '\0')?\
+                                       (&argv[0][1]) :\
+                                       (argc--, argv++, argv[0])))
+
+#endif
diff --git a/source/sdhcp/config.mk b/source/sdhcp/config.mk
new file mode 100644 (file)
index 0000000..b3ac9aa
--- /dev/null
@@ -0,0 +1,12 @@
+# sdhcp version
+VERSION   = 0.1
+
+PREFIX    = /usr/local
+DESTDIR   =
+MANPREFIX = $(PREFIX)/share/man
+
+CC        = cc
+LD        = $(CC)
+CPPFLAGS  = -D_BSD_SOURCE
+CFLAGS    = -Wall -Wextra -pedantic -std=c99 $(CPPFLAGS)
+LDFLAGS   = -s
diff --git a/source/sdhcp/sdhcp.1 b/source/sdhcp/sdhcp.1
new file mode 100644 (file)
index 0000000..ba964a1
--- /dev/null
@@ -0,0 +1,51 @@
+.Dd April 27, 2015
+.Dt SDHCP 1
+.Os
+.Sh NAME
+.Nm sdhcp
+.Nd a simple DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl e Ar program
+.Op Fl f
+.Op Fl i
+.Op Ar interface
+.Op Ar client-id
+.Sh DESCRIPTION
+.Nm
+is a simple, tiny DHCP client. It runs until it enters the "Bound"
+state, then forks to the background and runs as a daemon to keep
+the lease alive.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+don't change DNS in
+.Pa /etc/resolv.conf .
+.It Fl e Ar program
+run
+.Ar program .
+Variables will be set, see VARIABLES.
+.It Fl f
+run in foreground.
+.It Fl i
+don't change interface information such as an IP address.
+.El
+.Sh VARIABLES
+The following variables are set:
+.Bl -tag -width Ds
+.It Ev SERVER
+DHCP IP.
+.It Ev DNS
+DNS IP.
+.It Ev ROUTER
+router IP.
+.It Ev MASK
+network mask.
+.It Ev CLIENT
+your client IP.
+.El
+.Sh BUGS
+I'm sure there are plenty. It only currently supports a small subset of
+DHCP options, and has been untested on larger networks. It ignores most of
+the DHCP options it understands.
diff --git a/source/sdhcp/sdhcp.c b/source/sdhcp/sdhcp.c
new file mode 100644 (file)
index 0000000..464c64c
--- /dev/null
@@ -0,0 +1,501 @@
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "arg.h"
+#include "util.h"
+
+typedef struct bootp {
+       unsigned char op      [1];
+       unsigned char htype   [1];
+       unsigned char hlen    [1];
+       unsigned char hops    [1];
+       unsigned char xid     [4];
+       unsigned char secs    [2];
+       unsigned char flags   [2];
+       unsigned char ciaddr  [4];
+       unsigned char yiaddr  [4];
+       unsigned char siaddr  [4];
+       unsigned char giaddr  [4];
+       unsigned char chaddr  [16];
+       unsigned char sname   [64];
+       unsigned char file    [128];
+       unsigned char magic   [4];
+       unsigned char optdata [312-4];
+} Bootp;
+
+enum {
+       DHCPdiscover =       1,
+       DHCPoffer,
+       DHCPrequest,
+       DHCPdecline,
+       DHCPack,
+       DHCPnak,
+       DHCPrelease,
+       DHCPinform,
+       Timeout =          200,
+
+       Bootrequest =        1,
+       Bootreply =          2,
+       /* bootp flags */
+       Fbroadcast =   1 << 15,
+
+       OBpad =              0,
+       OBmask =             1,
+       OBrouter =           3,
+       OBnameserver =       5,
+       OBdnsserver =        6,
+       OBbaddr =           28,
+       ODipaddr =          50, /* 0x32 */
+       ODlease =           51,
+       ODoverload =        52,
+       ODtype =            53, /* 0x35 */
+       ODserverid =        54, /* 0x36 */
+       ODparams =          55, /* 0x37 */
+       ODmessage =         56,
+       ODmaxmsg =          57,
+       ODrenewaltime =     58,
+       ODrebindingtime =   59,
+       ODvendorclass =     60,
+       ODclientid =        61, /* 0x3d */
+       ODtftpserver =      66,
+       ODbootfile =        67,
+       OBend =            255,
+};
+
+enum { Broadcast, Unicast};
+
+Bootp bp;
+unsigned char magic[] = {99, 130, 83, 99};
+
+/* conf */
+static unsigned char xid[sizeof bp.xid];
+static unsigned char hwaddr[16];
+static time_t starttime;
+static char *ifname = "eth0";
+static unsigned char cid[16];
+static char *program = "";
+static int sock;
+/* sav */
+static unsigned char server[4];
+static unsigned char client[4];
+static unsigned char mask[4];
+static unsigned char router[4];
+static unsigned char dns[4];
+static unsigned long t1;
+
+static int dflag = 1; /* change DNS in /etc/resolv.conf ? */
+static int iflag = 1; /* set IP ? */
+static int fflag = 0; /* run in foreground */
+
+#define IP(a,b,c,d) (unsigned char[4]){a,b,c,d}
+
+static void
+hnput(unsigned char *dst, unsigned long long src, size_t n)
+{
+       unsigned int i;
+
+       for (i = 0; n--; i++)
+               dst[i] = (src >> (n * 8)) & 0xff;
+}
+
+static struct sockaddr *
+iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port)
+{
+       struct sockaddr_in *in = (struct sockaddr_in *)ifaddr;
+
+       in->sin_family = AF_INET;
+       in->sin_port = htons(port);
+       memcpy(&(in->sin_addr), ip, sizeof in->sin_addr);
+
+       return ifaddr;
+}
+
+/* sendto UDP wrapper */
+static ssize_t
+udpsend(unsigned char ip[4], int fd, void *data, size_t n)
+{
+       struct sockaddr addr;
+       socklen_t addrlen = sizeof addr;
+       ssize_t sent;
+
+       iptoaddr(&addr, ip, 67); /* bootp server */
+       if ((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1)
+               eprintf("sendto:");
+
+       return sent;
+}
+
+/* recvfrom UDP wrapper */
+static ssize_t
+udprecv(unsigned char ip[4], int fd, void *data, size_t n)
+{
+       struct sockaddr addr;
+       socklen_t addrlen = sizeof addr;
+       ssize_t r;
+
+       iptoaddr(&addr, ip, 68); /* bootp client */
+       if ((r = recvfrom(fd, data, n, 0, &addr, &addrlen)) == -1)
+               eprintf("recvfrom:");
+
+       return r;
+}
+
+static void
+setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4])
+{
+       struct ifreq ifreq;
+       struct rtentry rtreq;
+       int fd;
+
+       memset(&ifreq, 0, sizeof(ifreq));
+       memset(&rtreq, 0, sizeof(rtreq));
+
+       strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
+       iptoaddr(&(ifreq.ifr_addr), ip, 0);
+       if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1)
+               eprintf("can't set ip, socket:");
+       ioctl(fd, SIOCSIFADDR, &ifreq);
+       iptoaddr(&(ifreq.ifr_netmask), mask, 0);
+       ioctl(fd, SIOCSIFNETMASK, &ifreq);
+       ifreq.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST;
+       ioctl(fd, SIOCSIFFLAGS, &ifreq);
+       /* gw */
+       rtreq.rt_flags = (RTF_UP | RTF_GATEWAY);
+       iptoaddr(&(rtreq.rt_gateway), gateway, 0);
+       iptoaddr(&(rtreq.rt_genmask), IP(0, 0, 0, 0), 0);
+       iptoaddr(&(rtreq.rt_dst), IP(0, 0, 0, 0), 0);
+       ioctl(fd, SIOCADDRT, &rtreq);
+
+       close(fd);
+}
+
+static void
+cat(int dfd, char *src)
+{
+       char buf[BUFSIZ];
+       int n, fd;
+
+       if ((fd = open(src, O_RDONLY)) == -1)
+               return; /* can't read, but don't error out */
+       while ((n = read(fd, buf, sizeof buf)) > 0)
+               write(dfd, buf, n);
+       close(fd);
+}
+
+static void
+setdns(unsigned char dns[4])
+{
+       char buf[128];
+       int fd;
+
+       if ((fd = creat("/etc/resolv.conf", 0644)) == -1) {
+               weprintf("can't change /etc/resolv.conf:");
+               return;
+       }
+       cat(fd, "/etc/resolv.conf.head");
+       if (snprintf(buf, sizeof(buf) - 1, "\nnameserver %d.%d.%d.%d\n",
+                dns[0], dns[1], dns[2], dns[3]) > 0)
+               write(fd, buf, strlen(buf));
+       cat(fd, "/etc/resolv.conf.tail");
+       close(fd);
+}
+
+static void
+optget(Bootp *bp, void *data, int opt, int n)
+{
+       unsigned char *p = bp->optdata;
+       unsigned char *top = ((unsigned char *)bp) + sizeof *bp;
+       int code, len;
+
+       while (p < top) {
+               code = *p++;
+               if (code == OBpad)
+                       continue;
+               if (code == OBend || p == top)
+                       break;
+               len = *p++;
+               if (len > top - p)
+                       break;
+               if (code == opt) {
+                       memcpy(data, p, MIN(len, n));
+                       break;
+               }
+               p += len;
+       }
+}
+
+static unsigned char *
+optput(unsigned char *p, int opt, unsigned char *data, size_t len)
+{
+       *p++ = opt;
+       *p++ = (unsigned char)len;
+       memcpy(p, data, len);
+
+       return p + len;
+}
+
+static unsigned char *
+hnoptput(unsigned char *p, int opt, long long data, size_t len)
+{
+       *p++ = opt;
+       *p++ = (unsigned char)len;
+       hnput(p, data, len);
+
+       return p + len;
+}
+
+static void
+dhcpsend(int type, int how)
+{
+       unsigned char *ip, *p;
+
+       memset(&bp, 0, sizeof bp);
+       hnput(bp.op, Bootrequest, 1);
+       hnput(bp.htype, 1, 1);
+       hnput(bp.hlen, 6, 1);
+       memcpy(bp.xid, xid, sizeof xid);
+       hnput(bp.flags, Fbroadcast, sizeof bp.flags);
+       hnput(bp.secs, time(NULL) - starttime, sizeof bp.secs);
+       memcpy(bp.magic, magic, sizeof bp.magic);
+       memcpy(bp.chaddr, hwaddr, sizeof bp.chaddr);
+       p = bp.optdata;
+       p = hnoptput(p, ODtype, type, 1);
+       p = optput(p, ODclientid, cid, sizeof(cid));
+
+       switch(type) {
+       case DHCPdiscover:
+               break;
+       case DHCPrequest:
+               /* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */
+               p = hnoptput(p, ODlease, t1, sizeof t1);
+               p = optput(p, ODipaddr, client, sizeof client);
+               p = optput(p, ODserverid, server, sizeof server);
+               break;
+       case DHCPrelease:
+               memcpy(bp.ciaddr, client, sizeof client);
+               p = optput(p, ODipaddr, client, sizeof client);
+               p = optput(p, ODserverid, server, sizeof server);
+               break;
+       }
+       *p++ = OBend;
+
+       ip = (how == Broadcast) ? IP(255, 255, 255, 255) : server;
+       udpsend(ip, sock, &bp, p - (unsigned char *)&bp);
+}
+
+static int
+dhcprecv(void)
+{
+       unsigned char type;
+       struct pollfd pfd;
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = sock;
+       pfd.events = POLLIN;
+
+       memset(&bp, 0, sizeof bp);
+       if (poll(&pfd, 1, -1) == -1) {
+               if (errno != EINTR)
+                       eprintf("poll:");
+               else
+                       return Timeout;
+       }
+       udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof bp);
+       optget(&bp, &type, ODtype, sizeof type);
+
+       return type;
+}
+
+static void
+acceptlease(void)
+{
+       char buf[128];
+
+       if (iflag)
+               setip(client, mask, router);
+       if (dflag)
+               setdns(dns);
+       if (*program) {
+               snprintf(buf, sizeof(buf), "%d.%d.%d.%d", server[0], server[1], server[2], server[3]);
+               setenv("SERVER", buf, 1);
+               snprintf(buf, sizeof(buf), "%d.%d.%d.%d", client[0], client[1], client[2], client[3]);
+               setenv("CLIENT", buf, 1);
+               snprintf(buf, sizeof(buf), "%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]);
+               setenv("MASK", buf, 1);
+               snprintf(buf, sizeof(buf), "%d.%d.%d.%d", router[0], router[1], router[2], router[3]);
+               setenv("ROUTER", buf, 1);
+               snprintf(buf, sizeof(buf), "%d.%d.%d.%d", dns[0], dns[1], dns[2], dns[3]);
+               setenv("DNS", buf, 1);
+               system(program);
+       }
+       alarm(t1);
+}
+
+static void
+run(void)
+{
+       int forked = 0;
+
+Init:
+       dhcpsend(DHCPdiscover, Broadcast);
+       alarm(1);
+       goto Selecting;
+Selecting:
+       switch(dhcprecv()) {
+       case DHCPoffer:
+               alarm(0);
+               memcpy(client, bp.yiaddr, sizeof client);
+               optget(&bp, server, ODserverid, sizeof server);
+               optget(&bp, mask, OBmask, sizeof mask);
+               optget(&bp, router, OBrouter, sizeof router);
+               optget(&bp, dns, OBdnsserver, sizeof dns);
+               optget(&bp, &t1, ODlease, sizeof t1);
+               t1 = ntohl(t1);
+               dhcpsend(DHCPrequest, Broadcast);
+               goto Requesting;
+       case Timeout:
+               goto Init;
+       default:
+               goto Selecting;
+       }
+Requesting:
+       switch(dhcprecv()) {
+       case DHCPoffer:
+               goto Requesting; /* ignore other offers. */
+       case DHCPack:
+               acceptlease();
+               goto Bound;
+       }
+Bound:
+       fputs("Congrats! You should be on the 'net.\n", stdout);
+       if (!fflag && !forked) {
+               if (fork())
+                       exit(0);
+               forked = 1;
+       }
+       switch (dhcprecv()) {
+       case DHCPoffer:
+       case DHCPack:
+       case DHCPnak:
+               goto Bound; /* discard offer, ACK or NAK */
+       case Timeout:
+               dhcpsend(DHCPrequest, Unicast);
+               goto Renewing;
+       }
+Renewing:
+       switch(dhcprecv()) {
+       case DHCPack:
+               acceptlease();
+               goto Bound;
+       case DHCPnak:
+               goto Init;
+       case Timeout:
+               dhcpsend(DHCPrequest, Broadcast);
+               goto Rebinding;
+       }
+Rebinding:
+       switch(dhcprecv()) {
+       case DHCPnak: /* lease expired */
+               goto Init;
+       case DHCPack:
+               acceptlease();
+               goto Bound;
+       }
+}
+
+static
+void nop(int unused)
+{
+       (void) unused;
+}
+
+static
+void cleanexit(int unused)
+{
+       (void) unused;
+       dhcpsend(DHCPrelease, Unicast);
+       exit(0);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-d] [-e program] [-f] [-i] [ifname] [clientid]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int bcast = 1;
+       struct ifreq ifreq;
+       struct sockaddr addr;
+       int rnd;
+
+       ARGBEGIN {
+       case 'd': /* don't update DNS in /etc/resolv.conf */
+               dflag = 0;
+               break;
+       case 'e': /* run program */
+               program = EARGF(usage());
+               break;
+       case 'f': /* run in foreground */
+               fflag = 1;
+               break;
+       case 'i': /* don't set ip */
+               iflag = 0;
+               break;
+       default:
+               usage();
+               break;
+       } ARGEND;
+
+       if (argc)
+               ifname = argv[0]; /* interface name */
+       if (argc >= 2)
+               strlcpy(cid, argv[1], sizeof(cid)); /* client-id */
+
+       memset(&ifreq, 0, sizeof(ifreq));
+       signal(SIGALRM, nop);
+       signal(SIGTERM, cleanexit);
+
+       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+               eprintf("socket:");
+       if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof bcast) == -1)
+               eprintf("setsockopt:");
+
+       strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
+       ioctl(sock, SIOCGIFINDEX, &ifreq);
+       if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof ifreq) == -1)
+               eprintf("setsockopt:");
+       iptoaddr(&addr, IP(255, 255, 255, 255), 68);
+       if (bind(sock, (void*)&addr, sizeof addr) != 0)
+               eprintf("bind:");
+       ioctl(sock, SIOCGIFHWADDR, &ifreq);
+       memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof ifreq.ifr_hwaddr.sa_data);
+       if (!cid[0])
+               memcpy(cid, hwaddr, sizeof(cid));
+
+       if ((rnd = open("/dev/urandom", O_RDONLY)) == -1)
+               eprintf("can't open /dev/urandom to generate unique transaction identifier:");
+       read(rnd, xid, sizeof xid);
+       close(rnd);
+
+       starttime = time(NULL);
+       run();
+
+       return 0;
+}
diff --git a/source/sdhcp/util.h b/source/sdhcp/util.h
new file mode 100644 (file)
index 0000000..e3b99c8
--- /dev/null
@@ -0,0 +1,9 @@
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define bpdump(p,n) 1
+
+#undef strlcpy
+size_t strlcpy(char *, const char *, size_t);
+
+void weprintf(const char *, ...);
+void eprintf(const char *, ...);
+void enprintf(int, const char *, ...);
diff --git a/source/sdhcp/util/eprintf.c b/source/sdhcp/util/eprintf.c
new file mode 100644 (file)
index 0000000..4d8f726
--- /dev/null
@@ -0,0 +1,65 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../util.h"
+
+char *argv0;
+
+static void venprintf(int, const char *, va_list);
+
+void
+eprintf(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       venprintf(1, fmt, ap);
+       va_end(ap);
+}
+
+void
+enprintf(int status, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       venprintf(status, fmt, ap);
+       va_end(ap);
+}
+
+void
+venprintf(int status, const char *fmt, va_list ap)
+{
+       if (strncmp(fmt, "usage", strlen("usage")))
+               fprintf(stderr, "%s: ", argv0);
+
+       vfprintf(stderr, fmt, ap);
+
+       if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+               fputc(' ', stderr);
+               perror(NULL);
+       }
+
+       exit(status);
+}
+
+void
+weprintf(const char *fmt, ...)
+{
+       va_list ap;
+
+       if (strncmp(fmt, "usage", strlen("usage")))
+               fprintf(stderr, "%s: ", argv0);
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+               fputc(' ', stderr);
+               perror(NULL);
+       }
+}
diff --git a/source/sdhcp/util/strlcpy.c b/source/sdhcp/util/strlcpy.c
new file mode 100644 (file)
index 0000000..62fb7f6
--- /dev/null
@@ -0,0 +1,32 @@
+/* Taken from OpenBSD */
+#include <sys/types.h>
+#include <string.h>
+#include "../util.h"
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+        char *d = dst;
+        const char *s = src;
+        size_t n = siz;
+        /* Copy as many bytes as will fit */
+        if (n != 0) {
+                while (--n != 0) {
+                        if ((*d++ = *s++) == '\0')
+                                break;
+                }
+        }
+        /* Not enough room in dst, add NUL and traverse rest of src */
+        if (n == 0) {
+                if (siz != 0)
+                        *d = '\0';              /* NUL-terminate dst */
+                while (*s++)
+                        ;
+        }
+        return(s - src - 1);    /* count does not include NUL */
+}