#if 0 /* -*- mode: c; c-file-style: "linux"; tab-width: 8; -*- set -euf; trg=${0##*''/}; trg=${trg%.c}; test ! -e "$trg" || rm "$trg" WARN="-Wall -Wstrict-prototypes -Winit-self -Wformat=2" # -pedantic WARN="$WARN -Wcast-align -Wpointer-arith " # -Wfloat-equal #-Werror WARN="$WARN -Wextra -Wwrite-strings -Wcast-qual -Wshadow" # -Wconversion WARN="$WARN -Wmissing-include-dirs -Wundef -Wbad-function-cast -Wlogical-op" WARN="$WARN -Waggregate-return -Wold-style-definition" WARN="$WARN -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls" WARN="$WARN -Wnested-externs -Winline -Wvla -Woverlength-strings -Wpadded" case ${1-} in '') set x -O2; shift; esac #case ${1-} in '') set x -ggdb; shift; esac set -x; exec ${CC:-gcc} -std=c99 $WARN "$@" -o "$trg" "$0" exit $? */ #endif // Last Modified: Sun 21 May 2017 19:39:10 +0300 too /* The best parts of this sw were taken from http://core.suckless.org/sdhcp (git://git.2f30.org/sdhcp.git), converted to one-file version. This is used as a base for amitcp-dhcpconfig.c -- a dhcp client for Amiga. This program code can be executed as shell script on unix-like systems... This software has the same MIT/X Consortium License as sdhcp: (C) 2012 David Galos (galosd83 (at) students.rowan.edu) (C) 2014-2015 Hiltjo Posthuma (C) 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. 2017 Tomi Ollila -- too a"t iki piste fi */ #if defined (__GNUC__) && __GNUC__ >= 4 #pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" #pragma GCC diagnostic ignored "-Wpointer-sign" #pragma GCC diagnostic ignored "-Wimplicit-function-declaration" #pragma GCC diagnostic ignored "-Wshadow" #pragma GCC diagnostic ignored "-Wunused-parameter" #define _DEFAULT_SOURCE #define GCCATTR(a) __attribute__(a) #else #define GCCATTR(a) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // -- begin embedded parts of util.h -- // #define MIN(a,b) (((a)<(b))?(a):(b)) #define bpdump(p,n) 1 // --- end embedded parts of util.h --- // // -- begin embedded arg.h -- // /* * 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 // --- end embedded arg.h --- // #if 0 } // for emacs indentation... #endif // extras -- just to work w/ this sw char * argv0; static void xstrlcpy(char *dst, const char * src, size_t siz) { strncpy(dst, src, siz); dst[siz] = '\0'; } #define weprintf(s) fprintf(stderr, (s)) #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" GCCATTR((noreturn)) GCCATTR((format(printf, 1, 2))) static void eprintf(const char * format, ...) { int err = errno; va_list ap; if (strncmp(format, "usage", sizeof("usage") - 1) != 0) fprintf(stderr, "%s: ", argv0); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); if (format[strlen(format) - 1] == ':') fprintf(stderr, " %s.\n", strerror(err)); exit(1); } #pragma GCC diagnostic pop // end extras 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 uint32_t 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, uint32_t 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)); xstrlcpy(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, uint32_t 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) xstrlcpy(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:"); xstrlcpy(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; }