From 132f78922054db876f2856f3057dcdafd6a4e12b Mon Sep 17 00:00:00 2001 From: Mike Lowis Date: Wed, 11 May 2016 12:04:49 -0400 Subject: [PATCH] Added sdhcp source and rules file --- Makefile | 1 + source/sdhcp/LICENSE | 23 ++ source/sdhcp/Makefile | 65 +++++ source/sdhcp/Rules.mk | 20 ++ source/sdhcp/TODO | 38 +++ source/sdhcp/arg.h | 63 +++++ source/sdhcp/config.mk | 12 + source/sdhcp/sdhcp.1 | 51 ++++ source/sdhcp/sdhcp.c | 501 ++++++++++++++++++++++++++++++++++++ source/sdhcp/util.h | 9 + source/sdhcp/util/eprintf.c | 65 +++++ source/sdhcp/util/strlcpy.c | 32 +++ 12 files changed, 880 insertions(+) create mode 100644 source/sdhcp/LICENSE create mode 100644 source/sdhcp/Makefile create mode 100644 source/sdhcp/Rules.mk create mode 100644 source/sdhcp/TODO create mode 100644 source/sdhcp/arg.h create mode 100644 source/sdhcp/config.mk create mode 100644 source/sdhcp/sdhcp.1 create mode 100644 source/sdhcp/sdhcp.c create mode 100644 source/sdhcp/util.h create mode 100644 source/sdhcp/util/eprintf.c create mode 100644 source/sdhcp/util/strlcpy.c diff --git a/Makefile b/Makefile index 29090dbc..f7da11f5 100644 --- 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 index 00000000..67aa74f6 --- /dev/null +++ b/source/sdhcp/LICENSE @@ -0,0 +1,23 @@ +MIT/X Consortium License + +© 2012 David Galos (galosd83 (at) students.rowan.edu) +© 2014-2015 Hiltjo Posthuma +© 2015 Michael Forney + +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 index 00000000..7126a36d --- /dev/null +++ b/source/sdhcp/Makefile @@ -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 index 00000000..a536b8a0 --- /dev/null +++ b/source/sdhcp/Rules.mk @@ -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 index 00000000..be4d6cf0 --- /dev/null +++ b/source/sdhcp/TODO @@ -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 index 00000000..4df77a70 --- /dev/null +++ b/source/sdhcp/arg.h @@ -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 index 00000000..b3ac9aab --- /dev/null +++ b/source/sdhcp/config.mk @@ -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 index 00000000..ba964a1a --- /dev/null +++ b/source/sdhcp/sdhcp.1 @@ -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 index 00000000..464c64c9 --- /dev/null +++ b/source/sdhcp/sdhcp.c @@ -0,0 +1,501 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..e3b99c8e --- /dev/null +++ b/source/sdhcp/util.h @@ -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 index 00000000..4d8f726b --- /dev/null +++ b/source/sdhcp/util/eprintf.c @@ -0,0 +1,65 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#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 index 00000000..62fb7f69 --- /dev/null +++ b/source/sdhcp/util/strlcpy.c @@ -0,0 +1,32 @@ +/* Taken from OpenBSD */ +#include +#include +#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 */ +} -- 2.49.0