Initial import of http://galos.no-ip.org/sdhcp - sdhcp - simple dhcp client | |
git clone git://git.codemadness.org/sdhcp | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit a91f359dd69354befd8c70a89e0effd8891e1644 | |
Author: sin <[email protected]> | |
Date: Wed, 25 Sep 2013 12:14:00 +0100 | |
Initial import of http://galos.no-ip.org/sdhcp | |
Diffstat: | |
A Makefile | 14 ++++++++++++++ | |
A debug.c | 139 ++++++++++++++++++++++++++++++ | |
A sdhcp.8 | 16 ++++++++++++++++ | |
A sdhcp.c | 437 +++++++++++++++++++++++++++++… | |
4 files changed, 606 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -0,0 +1,14 @@ | |
+DESTDIR= | |
+sdhcp: sdhcp.c debug.c | |
+ $(CC) -O2 -o $@ sdhcp.c -static | |
+debug: sdhcp.c debug.c | |
+ $(CC) -DDEBUG -o sdhcp sdhcp.c -static | |
+ | |
+all: sdhcp | |
+ | |
+install: all | |
+ install -s sdhcp $(DESTDIR)/sbin | |
+ gzip -c sdhcp.8 > /usr/share/man/man8/sdhcp.8.gz | |
+ | |
+clean: | |
+ rm -f sdhcp ?*~ | |
diff --git a/debug.c b/debug.c | |
@@ -0,0 +1,139 @@ | |
+#include<stdarg.h> | |
+#ifdef DEBUG | |
+unsigned short nhgets(uchar c[2]){ | |
+ return ((c[0]<<8) + c[1])&0xffff; | |
+} | |
+unsigned long nhgetl(uchar c[4]){ | |
+ return (nhgets(c)<<16) + nhgets(c+2); | |
+} | |
+char *ipstr(uchar *ip){ | |
+ char * ch = malloc(3*4+3+10); | |
+ sprintf(ch, "%d.%d.%d.%d", ip[0],ip[1],ip[2],ip[3]); | |
+ return ch; | |
+} | |
+#endif | |
+ | |
+void | |
+dbgprintf(char *str, ...) | |
+{ | |
+#ifdef DEBUG | |
+ va_list ap; | |
+ va_start(ap, str); | |
+ vfprintf(stderr, str, ap); | |
+ va_end(ap); | |
+#endif | |
+} | |
+ | |
+void | |
+bpdump(uchar *p, int n) | |
+{ | |
+#ifdef DEBUG | |
+ int len, i, code; | |
+ Bootp *bp; | |
+// Udphdr *up; | |
+ | |
+ bp = (Bootp*)p; | |
+ // up = (Udphdr*)bp->udphdr; | |
+ | |
+ if(n < bp->magic - p) { | |
+ fprintf(stderr, "dhcpclient: short bootp packet"); | |
+ return; | |
+ } | |
+ char *types[] = {"discover", "offer", "request", | |
+ "decline", "ack", "nak", "release", "inform"}; | |
+ uchar type; | |
+ optget(bp, &type, ODtype, sizeof type); | |
+ fprintf(stderr, "DHCP%s\n", types[type-1]); | |
+ // fprintf(stderr, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr, | |
+ // nhgets(up->lport), up->raddr, nhgets(up->rport)); | |
+ fprintf(stderr, "op=%d htype=%d hlen=%d hops=%d\n", *bp->op, *bp->htype, | |
+ * bp->hlen, *bp->hops); | |
+ fprintf(stderr, "xid=%x secs=%d flags=%x\n", nhgetl(bp->xid), | |
+ nhgets(bp->secs), nhgets(bp->flags)); | |
+ fprintf(stderr, "ciaddr=%s yiaddr=%s siaddr=%s giaddr=%s\n", | |
+ ipstr(bp->ciaddr), ipstr(bp->yiaddr), ipstr(bp->siaddr), ipstr(bp->… | |
+ fprintf(stderr, "chaddr="); | |
+ for(i=0; i<15; i++) | |
+ fprintf(stderr, "%.2x:", bp->chaddr[i]); | |
+ fprintf(stderr, "%.2x\n", bp->chaddr[15]); | |
+ fprintf(stderr, "sname=%s\n", bp->sname); | |
+ fprintf(stderr, "file = %s\n", bp->file); | |
+ | |
+ n -= bp->magic - p; | |
+ p = bp->magic; | |
+ | |
+ if(n < 4) | |
+ return; | |
+ if(memcmp(magic, p, 4) != 0) | |
+ fprintf(stderr, "dhcpclient: bad opt magic %#x %#x %#x %#x\n", | |
+ p[0], p[1], p[2], p[3]); | |
+ p += 4; | |
+ n -= 4; | |
+ | |
+ while(n>0) { | |
+ code = *p++; | |
+ n--; | |
+ if(code == OBpad) | |
+ continue; | |
+ if(code == OBend) | |
+ break; | |
+ if(n == 0) { | |
+ fprintf(stderr, " bad option: %d", code); | |
+ return; | |
+ } | |
+ len = *p++; | |
+ n--; | |
+ if(len > n) { | |
+ fprintf(stderr, " bad option: %d", code); | |
+ return; | |
+ } | |
+ switch(code) { | |
+ default: | |
+ fprintf(stderr, "unknown option %d\n", code); | |
+ for(i = 0; i<len; i++) | |
+ fprintf(stderr, "%x ", p[i]); | |
+ fprintf(stderr, "\n"); | |
+ break; | |
+ case ODtype: | |
+ fprintf(stderr, "DHCP type %d\n", p[0]); | |
+ break; | |
+ case ODclientid: | |
+ fprintf(stderr, "client id="); | |
+ for(i = 0; i<len; i++) | |
+ fprintf(stderr, "%x ", p[i]); | |
+ fprintf(stderr, "\n"); | |
+ break; | |
+ case ODlease: | |
+ fprintf(stderr, "lease=%d sec\n", nhgetl(p)); | |
+ break; | |
+ case ODserverid: | |
+ fprintf(stderr, "server id=%s\n", ipstr(p)); | |
+ break; | |
+ case OBmask: | |
+ fprintf(stderr, "mask=%s\n", ipstr(p)); | |
+ break; | |
+ case OBrouter: | |
+ fprintf(stderr, "router=%s\n", ipstr(p)); | |
+ break; | |
+ case ODipaddr: | |
+ fprintf(stderr, "ip addr=%s\n", ipstr(p)); | |
+ break; | |
+ case OBdnsserver: | |
+ fprintf(stderr, "dns=%s\n", ipstr(p)); | |
+ break; | |
+ case OBbaddr: | |
+ fprintf(stderr, "broadcast=%s\n", ipstr(p)); | |
+ break; | |
+ case ODrenewaltime: | |
+ fprintf(stderr, "renew time=%d sec\n", nhgetl(p)); | |
+ break; | |
+ case ODrebindingtime: | |
+ fprintf(stderr, "rebind time=%d sec\n", nhgetl(p)); | |
+ break; | |
+ } | |
+ p += len; | |
+ n -= len; | |
+ } | |
+#endif | |
+ } | |
+ | |
diff --git a/sdhcp.8 b/sdhcp.8 | |
@@ -0,0 +1,16 @@ | |
+.TH SDHCP 1 | |
+.SH NAME | |
+sdhcp \- a simple dhcp client | |
+.SH SYNOPSIS | |
+.B sdhcp | |
+.RB [interface] | |
+.SH DESCRIPTION | |
+sdchp 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 BUGS | |
+I'm sure there's plenty. It only currently supports a small subset of | |
+dhcp options, and has been untested on larger networks. It ignores most of | |
+the dchp options it understands. Send bug reports to me! | |
+.SH AUTHOR | |
+David Galos (galosd83 (at) students.rowan.edu) | |
diff --git a/sdhcp.c b/sdhcp.c | |
@@ -0,0 +1,437 @@ | |
+#include<sys/socket.h> | |
+#include<sys/ioctl.h> | |
+#include<netinet/in.h> | |
+#include<net/if.h> | |
+#include<net/route.h> | |
+#include<signal.h> | |
+#include<sys/poll.h> | |
+#include<errno.h> | |
+#include<fcntl.h> | |
+#include<stdio.h> | |
+#include<stdlib.h> | |
+#include<string.h> | |
+ | |
+#define MIN(a,b) (((a)<(b))?(a):(b)) | |
+ | |
+typedef unsigned char uchar; | |
+typedef struct Bootp Bootp; | |
+struct Bootp { | |
+ uchar op [1]; | |
+ uchar htype [1]; | |
+ uchar hlen [1]; | |
+ uchar hops [1]; | |
+ uchar xid [4]; | |
+ uchar secs [2]; | |
+ uchar flags [2]; | |
+ uchar ciaddr [4]; | |
+ uchar yiaddr [4]; | |
+ uchar siaddr [4]; | |
+ uchar giaddr [4]; | |
+ uchar chaddr [16]; | |
+ uchar sname [64]; | |
+ uchar file [128]; | |
+ uchar magic [4]; | |
+ uchar optdata [312-4]; | |
+}; | |
+ | |
+void bpdump(uchar *p, int n); | |
+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; | |
+uchar magic[] = {99, 130, 83, 99}; | |
+ | |
+//struct conf{ | |
+ uchar xid[sizeof bp.xid]; | |
+ uchar hwaddr[16]; | |
+ time_t starttime; | |
+ char *ifname = "eth0"; | |
+ char *cid = "vaio.12340"; | |
+ int sock; | |
+//} var; | |
+//struct sav{ | |
+ uchar server[4]; | |
+ uchar client[4]; | |
+ uchar mask[4]; | |
+ uchar router[4]; | |
+ uchar dns[4]; | |
+ unsigned long t1; | |
+ unsigned long t2; | |
+//} sav; | |
+ | |
+#define IP(...) (uchar[4]){__VA_ARGS__} | |
+ | |
+static void | |
+die(char *str) | |
+{ | |
+ perror(str); | |
+ exit(EXIT_FAILURE); | |
+} | |
+ | |
+static void | |
+hnput(uchar *dst, unsigned long long src, int n) | |
+{ | |
+ int x; | |
+ for(x=0; n--; x++) | |
+ dst[x] = (src>>(n*8))&0xff; | |
+} | |
+ | |
+static struct sockaddr | |
+iptoaddr(uchar ip[4], int port) | |
+{ | |
+ struct sockaddr_in ifaddr; | |
+ ifaddr.sin_family=AF_INET; | |
+ ifaddr.sin_port = htons(port); | |
+ memcpy(&ifaddr.sin_addr, ip, sizeof ifaddr.sin_addr); | |
+ return *(struct sockaddr*)&ifaddr; | |
+} | |
+ | |
+#define UDPWRAPPER(name, func, port, hack) \ | |
+static int name(uchar ip[4], int fd, void *data, size_t n){\ | |
+ struct sockaddr addr = iptoaddr(ip, port);\ | |
+ int x, y = sizeof addr;\ | |
+ if((x=func(fd, data, n, 0, &addr, hack y))==-1)\ | |
+ die(#func);\ | |
+ return x;\ | |
+} | |
+UDPWRAPPER(udpsend, sendto, 67, ) | |
+UDPWRAPPER(udprecv, recvfrom, 68, &) | |
+ | |
+static void | |
+setip(uchar ip[4], uchar mask[4], uchar gateway[4]) | |
+{ | |
+ int fd, x; | |
+ struct ifreq ifreq = {0,}; | |
+ struct rtentry rtreq = {0,}; | |
+ | |
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); | |
+ strcpy(ifreq.ifr_name, ifname); | |
+ ifreq.ifr_addr = iptoaddr(ip, 0); | |
+ ioctl(fd, SIOCSIFADDR , &ifreq); | |
+ ifreq.ifr_netmask = iptoaddr(mask, 0); | |
+ ioctl(fd, SIOCSIFNETMASK , &ifreq); | |
+ ifreq.ifr_flags=IFF_UP|IFF_RUNNING|IFF_BROADCAST|IFF_MULTICAST; | |
+ ioctl(fd, SIOCSIFFLAGS , &ifreq); | |
+ | |
+ rtreq.rt_flags = (RTF_UP | RTF_GATEWAY); | |
+ rtreq.rt_gateway = iptoaddr(gateway, 0); | |
+ rtreq.rt_genmask = iptoaddr(IP(0,0,0,0), 0); | |
+ rtreq.rt_dst = iptoaddr(IP(0,0,0,0), 0); | |
+ ioctl(fd, SIOCADDRT , &rtreq); | |
+ | |
+ close(fd); | |
+} | |
+static void | |
+cat(int dfd, char *src) | |
+{ | |
+ char buf[4096]; | |
+ int n, sfd = open(src, O_RDONLY); | |
+ while((n=read(sfd, buf, sizeof buf))>0) | |
+ write(dfd, buf, n); | |
+ close(sfd); | |
+ | |
+} | |
+//use itoa not sprintf to make dietlibc happy. | |
+char* itoa(char * str, int x) | |
+{ | |
+ if(x==0){ | |
+ *str='0'; | |
+ return str+1; | |
+ } | |
+ int k = 1; | |
+ char *ep = str; | |
+ while(x/k > 0) | |
+ k*=10; | |
+ while((k/=10)>=1) | |
+ *ep++ = '0'+((x/k)%10); | |
+ *ep = '\0'; | |
+ return str+strlen(str); | |
+} | |
+static void | |
+setdns(uchar dns[4]) | |
+{ | |
+ char buf[128], *bp = buf; | |
+ int fd = creat("/etc/resolv.conf", 0644); | |
+ cat(fd, "/etc/resolv.conf.head"); | |
+ memcpy(buf, "\nnameserver ", 12), bp+=11; | |
+ *(bp=itoa(bp+1, dns[0])) = '.'; | |
+ *(bp=itoa(bp+1, dns[1])) = '.'; | |
+ *(bp=itoa(bp+1, dns[2])) = '.'; | |
+ *(bp=itoa(bp+1, dns[3])) = '\n'; | |
+ *++bp = '\0'; | |
+ write(fd, buf, strlen(buf)); | |
+ cat(fd, "/etc/resolv.conf.tail"); | |
+ close(fd); | |
+} | |
+ | |
+static uchar * | |
+optget(Bootp *bp, void *data, int opt, int n) | |
+{ | |
+ uchar *p = bp->optdata; | |
+ uchar *top = ((uchar*)bp)+sizeof *bp; | |
+ while(p<top){ | |
+ int code = *p++; | |
+ if(code==OBpad) | |
+ continue; | |
+ if(code==OBend || p==top) | |
+ break; | |
+ int len = *p++; | |
+ if(len > top-p) | |
+ break; | |
+ if(code==opt){ | |
+ memcpy(data, p, MIN(len, n)); | |
+ return p; | |
+ } | |
+ p+=len; | |
+ } | |
+} | |
+ | |
+static uchar * | |
+optput(uchar *p, int opt, uchar *data, int len) | |
+{ | |
+ *p++ = opt; | |
+ *p++ = (uchar)len; | |
+ memcpy(p, data, len); | |
+ return p+len; | |
+} | |
+static uchar* | |
+hnoptput(uchar *p, int opt, long long data, int len) | |
+{ | |
+ *p++=opt; | |
+ *p++ = (uchar)len; | |
+ hnput(p, data, len); | |
+ return p+len; | |
+} | |
+#include "debug.c" | |
+ | |
+static void | |
+dhcpsend(int type, int how) | |
+{ | |
+ dbgprintf("\nSending "); | |
+ 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); | |
+ uchar *p = bp.optdata; | |
+ p = hnoptput(p, ODtype, type, 1); | |
+ p = optput(p, ODclientid, cid, strlen(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; | |
+ bpdump((void*)&bp, p-(uchar*)&bp); | |
+ uchar *ip = (how==Broadcast)?IP(255,255,255,255):server; | |
+ udpsend(ip, sock, &bp, p-(uchar*)&bp); | |
+} | |
+ | |
+static int | |
+dhcprecv() | |
+{ | |
+ dbgprintf("\nReceiving "); | |
+ memset(&bp, 0, sizeof bp); | |
+ struct pollfd pfd = {sock, POLLIN}; | |
+ if(poll(&pfd, 1, -1)==-1){ | |
+ if(errno!=EINTR) | |
+ die("poll"); | |
+ else | |
+ return Timeout; | |
+ } | |
+ int x = udprecv(IP(255,255,255,255), sock, &bp, sizeof bp); | |
+ bpdump((void*)&bp, x); | |
+ uchar type; | |
+ optget(&bp, &type, ODtype, sizeof type); | |
+ return type; | |
+} | |
+ | |
+static void | |
+acceptlease() | |
+{ | |
+ setip(client, mask, router); | |
+ setdns(dns); | |
+ alarm(t1); | |
+} | |
+ | |
+static void | |
+run() | |
+{ | |
+#if 0 | |
+InitReboot: | |
+ //send DHCPrequest to old server | |
+ dhcpsend(DHCPrequest, Broadcasr); | |
+ goto Rebooting; | |
+Rebooting: | |
+ switch (dhcprecv()){ | |
+ case DHCPnak: | |
+ goto Init; | |
+ case DHCPack: | |
+ acceptoffer(); | |
+ goto Bound; | |
+ } | |
+#endif | |
+Init: | |
+ dbgprintf("\n\n------- Init ------\n"); | |
+ dhcpsend(DHCPdiscover, Broadcast); | |
+ alarm(1); | |
+ goto Selecting; | |
+Selecting: | |
+ dbgprintf("\n\n------- Selecting ------\n"); | |
+ 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: | |
+ dbgprintf("\n\n------- Requesting ------\n"); | |
+ switch(dhcprecv()){ | |
+ case DHCPoffer: | |
+ goto Requesting; //ignore other offers. | |
+// case DHCPack: //(and you don't want it)? | |
+// dhcpsend(DHCPdecline, Unicast); | |
+// goto Init; | |
+ case DHCPack: | |
+ acceptlease(); | |
+ goto Bound; | |
+ } | |
+Bound: | |
+ dbgprintf("\n\n------- Bound ------\n"); | |
+ write(1, "Congrats! You should be on the 'net.\n", 37); | |
+ if(fork()) | |
+ exit(0); | |
+ switch (dhcprecv()){ | |
+ case DHCPoffer: | |
+ case DHCPack: | |
+ case DHCPnak: | |
+ goto Bound; //discard offer, ack, or nak | |
+ case Timeout: | |
+ dhcpsend(DHCPrequest, Unicast); | |
+ goto Renewing; | |
+ } | |
+Renewing: | |
+ dbgprintf("\n\n------- Renewing ------\n"); | |
+ switch(dhcprecv()){ | |
+ case DHCPack: | |
+ acceptlease(); | |
+ goto Bound; | |
+ case DHCPnak: | |
+ //halt network; | |
+ goto Init; | |
+ case Timeout: //t2 expires: | |
+ dhcpsend(DHCPrequest, Broadcast); | |
+ goto Rebinding; | |
+ } | |
+Rebinding: | |
+ dbgprintf("\n\n------- Rebinding ------\n"); | |
+ switch(dhcprecv()){ | |
+ case DHCPnak: //lease expired | |
+ //halt network; | |
+ goto Init; | |
+ case DHCPack: | |
+ acceptlease(); | |
+ goto Bound; | |
+ } | |
+} | |
+ | |
+static void nop(int unused){ } | |
+static void cleanexit(int unused){ | |
+ dhcpsend(DHCPrelease, Unicast); | |
+ exit(0); | |
+} | |
+ | |
+int | |
+main(int argc, char *argv[]) | |
+{ | |
+ if(argc>2){ | |
+ write(2, "usage: sdhcp [inferface]\n",25); | |
+ exit(EXIT_FAILURE); | |
+ }if(argc==2) | |
+ ifname = argv[1]; | |
+ | |
+ signal(SIGALRM, nop); | |
+ signal(SIGTERM, cleanexit); | |
+ | |
+ if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
+ die("socket"); | |
+ int bcast = 1; | |
+ if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof bcast)==-… | |
+ die("setsockopt"); | |
+ struct ifreq ifreq = {0,}; | |
+ strcpy(ifreq.ifr_name, ifname); | |
+ ioctl(sock, SIOCGIFINDEX, &ifreq); | |
+ if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof ifreq)… | |
+ die("setsockopt"); | |
+ struct sockaddr addr = iptoaddr(IP(255,255,255,255), 68); | |
+ if(bind(sock, (void*)&addr, sizeof addr)!=0) | |
+ die("bind"); | |
+ ioctl(sock, SIOCGIFHWADDR, &ifreq); | |
+ memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof ifreq.ifr_hwaddr.sa_da… | |
+ int rnd = open("/dev/urandom", O_RDONLY); | |
+ read(rnd, xid, sizeof xid); | |
+ close(rnd); | |
+ | |
+ starttime = time(NULL); | |
+ run(); | |
+ return 0; | |
+} | |
+ |