home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 May / pcp151c.iso / misc / src / install / bootpc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-15  |  17.3 KB  |  697 lines

  1. /* This probably doesn't work on anything other then Ethernet! It may work
  2.    on PLIP as well, but ARCnet and Token Ring are unlikely at best. */
  3.  
  4. #include <errno.h>
  5. #include <net/if.h>
  6. #include <net/route.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <signal.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <sys/ioctl.h>
  14. #include <sys/time.h>
  15. #include <sys/types.h>
  16. #include <unistd.h>
  17.  
  18. #include "bootpc.h"
  19. #include "net.h"
  20.  
  21. #define NUM_RETRIES    3
  22.  
  23. #ifdef STANDALONE
  24. #define _(foo) ((foo))
  25. #include <stdarg.h>
  26. #else
  27. #include "log.h"
  28. #include "intl.h"
  29. #endif
  30.  
  31. #ifdef STANDALONE
  32. void logMessage(char * mess, ...) {
  33.     va_list args;
  34.  
  35.     va_start(args, mess);
  36.     vprintf(mess, args);
  37.     va_end(args);
  38.     puts("");
  39. }
  40. #endif
  41.  
  42. typedef int bp_int32;
  43. typedef short bp_int16;
  44.  
  45. #define BOOTP_OPTION_NETMASK        1
  46. #define BOOTP_OPTION_GATEWAY        3
  47. #define BOOTP_OPTION_DNS        6
  48. #define BOOTP_OPTION_HOSTNAME        12
  49. #define BOOTP_OPTION_BOOTFILE        13
  50. #define BOOTP_OPTION_DOMAIN        15
  51. #define BOOTP_OPTION_BROADCAST        28
  52.  
  53. #define DHCP_OPTION_REQADDR        50
  54. #define DHCP_OPTION_LEASE        51
  55. #define DHCP_OPTION_OVERLOAD        52
  56. #define DHCP_OPTION_TYPE        53
  57. #define DHCP_OPTION_SERVER        54
  58. #define DHCP_OPTION_T1            58
  59.  
  60. #define BOOTP_CLIENT_PORT    68
  61. #define BOOTP_SERVER_PORT    67
  62.  
  63. #define BOOTP_OPCODE_REQUEST    1
  64. #define BOOTP_OPCODE_REPLY    2
  65.  
  66. #define DHCP_TYPE_DISCOVER    1
  67. #define DHCP_TYPE_OFFER        2
  68. #define DHCP_TYPE_REQUEST    3
  69. #define DHCP_TYPE_DECLINE    4
  70. #define DHCP_TYPE_ACK        5
  71. #define DHCP_TYPE_NAK        6
  72. #define DHCP_TYPE_RELEASE    7
  73. #define DHCP_TYPE_INFORM    8
  74.  
  75. #define BOOTP_VENDOR_LENGTH    64
  76. #define DHCP_VENDOR_LENGTH    340
  77.  
  78. struct bootpRequest {
  79.     char opcode;
  80.     char hw;
  81.     char hwlength;
  82.     char hopcount;
  83.     bp_int32 id;
  84.     bp_int16 secs;
  85.     bp_int16 flags;
  86.     bp_int32 ciaddr, yiaddr, server_ip, bootp_gw_ip;
  87.     char hwaddr[16];
  88.     char servername[64];
  89.     char bootfile[128];
  90.     char vendor[DHCP_VENDOR_LENGTH];
  91. } ;
  92.  
  93. static const char vendCookie[] = { 99, 130, 83, 99, 255 };
  94.  
  95. static char * perrorstr(char * msg);
  96. static char * setupInterface(char * device, int s);
  97. static void parseReply(struct bootpRequest * breq, struct intfInfo * intf, 
  98.                struct netInfo * net);
  99. static char * prepareRequest(struct bootpRequest * breq,
  100.                  int sock, char * device, time_t startTime);
  101. static void initVendorCodes(struct bootpRequest * breq);
  102. static char * handleTransaction(int s, int timeout, struct bootpRequest * breq,
  103.                     struct bootpRequest * bresp, 
  104.                     struct sockaddr_in * serverAddr,
  105.                     struct sockaddr_in * respondant,
  106.                 int dhcpType);
  107. static int dhcpMessageType(struct bootpRequest * response);
  108.  
  109. static char * setupInterface(char * device, int s) {
  110.     struct sockaddr_in * addrp;
  111.     struct ifreq req;
  112.     struct rtentry route;
  113.     int true = 1;
  114.  
  115.     addrp = (struct sockaddr_in *) &req.ifr_addr;
  116.  
  117.     strcpy(req.ifr_name, device);
  118.     addrp->sin_family = AF_INET;
  119.     addrp->sin_port = 0;
  120.     memset(&addrp->sin_addr, 0, sizeof(addrp->sin_addr));
  121.     memset(&route, 0, sizeof(route));
  122.     memcpy(&route.rt_dst, addrp, sizeof(route.rt_dst));
  123.  
  124.     if (ioctl(s, SIOCSIFADDR, &req))
  125.     return perrorstr("SIOCSIFADDR");
  126.  
  127.     if (ioctl(s, SIOCSIFNETMASK, &req))
  128.     return perrorstr("SIOCSIFNETMASK");
  129.  
  130.     /* the broadcast address is 255.255.255.255 */
  131.     memset(&addrp->sin_addr,255 , sizeof(addrp->sin_addr));
  132.     if (ioctl(s, SIOCSIFBRDADDR, &req))
  133.     return perrorstr("SIOCSIFBRDADDR");
  134.  
  135.  
  136.     req.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
  137.     if (ioctl(s, SIOCSIFFLAGS, &req))
  138.     return perrorstr("SIOCSIFFLAGS");
  139.  
  140.     route.rt_dev = device;
  141.     route.rt_flags = RTF_UP;
  142.  
  143.     if (ioctl(s, SIOCADDRT, &route)) 
  144.     return perrorstr("SIOCSADDRT");
  145.  
  146.     if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &true, 1)) {
  147.     close(s);
  148.     return perrorstr("setsockopt");
  149.     }
  150.  
  151.     return NULL;
  152. }
  153.  
  154. static int dhcpMessageType(struct bootpRequest * response) {
  155.     unsigned char * chptr;
  156.     unsigned char option, length;
  157.    
  158.     chptr = response->vendor;
  159.  
  160.     chptr += 4;
  161.     while (*chptr != 0xFF) {
  162.     option = *chptr++;
  163.     if (!option) continue;
  164.     length = *chptr++;
  165.     if (option == DHCP_OPTION_TYPE)
  166.         return *chptr;
  167.  
  168.     chptr += length;
  169.     }
  170.  
  171.     return -1;
  172. }
  173.  
  174. void setMissingIpInfo(struct intfInfo * intf) {
  175.     bp_int32 ipNum = *((bp_int32 *) &intf->ip);
  176.     bp_int32 nmNum = *((bp_int32 *) &intf->netmask);
  177.     bp_int32 ipRealNum = ntohl(ipNum);
  178.  
  179.     if (!(intf->set & INTFINFO_HAS_NETMASK)) {
  180.     if (((ipRealNum & 0xFF000000) >> 24) <= 127)
  181.         nmNum = 0xFF000000;
  182.     else if (((ipRealNum & 0xFF000000) >> 24) <= 191)
  183.         nmNum = 0xFFFF0000;
  184.     else 
  185.         nmNum = 0xFFFFFF00;
  186.     *((bp_int32 *) &intf->netmask) = nmNum = htonl(nmNum);
  187.     }
  188.  
  189.     if (!(intf->set & INTFINFO_HAS_BROADCAST))
  190.     *((bp_int32 *) &intf->broadcast) = (ipNum & nmNum) | ~(nmNum);
  191.  
  192.     if (!(intf->set & INTFINFO_HAS_NETWORK))
  193.     *((bp_int32 *) &intf->network) = ipNum & nmNum;
  194.  
  195.     intf->set |= INTFINFO_HAS_BROADCAST | INTFINFO_HAS_NETWORK | 
  196.          INTFINFO_HAS_NETMASK;
  197. }
  198.  
  199. static void parseReply(struct bootpRequest * breq, struct intfInfo * intf, 
  200.                struct netInfo * net) {
  201.     unsigned int i;
  202.     unsigned char * chptr;
  203.     unsigned char option, length;
  204.  
  205.     i = ~(INTFINFO_HAS_IP | INTFINFO_HAS_NETMASK | INTFINFO_HAS_NETWORK |
  206.       INTFINFO_HAS_BROADCAST);
  207.     intf->set &= i;
  208.     intf->manuallySet &= i;
  209.  
  210.     if (strlen(breq->bootfile)) {
  211.     intf->bootFile = strdup(breq->bootfile);
  212.     intf->set |= INTFINFO_HAS_BOOTFILE;
  213.     } else {
  214.     intf->set &= ~INTFINFO_HAS_BOOTFILE;
  215.     }
  216.  
  217.     if (breq->server_ip) {
  218.     intf->set |= INTFINFO_HAS_BOOTSERVER;
  219.     memcpy(&intf->bootServer, &breq->server_ip, sizeof(breq->server_ip));
  220.     }
  221.  
  222.     memcpy(&intf->ip, &breq->yiaddr, 4);
  223.     intf->set |= INTFINFO_HAS_IP;
  224.  
  225.     chptr = breq->vendor;
  226.     chptr += 4;
  227.     while (*chptr != 0xFF) {
  228.     option = *chptr++;
  229.     if (!option) continue;
  230.     length = *chptr++;
  231.  
  232.     switch (option) {
  233.         case BOOTP_OPTION_DNS:
  234.         net->numDns = 0;
  235.         for (i = 0; i < length; i += 4)
  236.             memcpy(&net->dnsServers[net->numDns++], chptr + i, 4);
  237.         net->set |= NETINFO_HAS_DNS;
  238.         break;
  239.  
  240.         case BOOTP_OPTION_NETMASK:
  241.         memcpy(&intf->netmask, chptr, 4);
  242.         intf->set |= INTFINFO_HAS_NETMASK;
  243.         break;
  244.  
  245.         case BOOTP_OPTION_DOMAIN:
  246.         net->domain = malloc(length + 1);
  247.         memcpy(net->domain, chptr, length);
  248.         net->domain[length] = '\0';
  249.         net->set |= NETINFO_HAS_DOMAIN;
  250.         break;
  251.  
  252.         case BOOTP_OPTION_BROADCAST:
  253.         memcpy(&intf->broadcast, chptr, 4);
  254.         intf->set |= INTFINFO_HAS_BROADCAST;
  255.         break;
  256.  
  257.         case BOOTP_OPTION_GATEWAY:
  258.         memcpy(&net->gateway, chptr, 4);
  259.         net->set |= NETINFO_HAS_GATEWAY;
  260.         break;
  261.  
  262.         case BOOTP_OPTION_HOSTNAME:
  263.         net->hostname = malloc(length + 1);
  264.         memcpy(net->hostname, chptr, length);
  265.         net->hostname[length] = '\0';
  266.         net->set |= NETINFO_HAS_HOSTNAME;
  267.         break;
  268.  
  269.         case BOOTP_OPTION_BOOTFILE:
  270.         /* we ignore this right now */
  271.         break;
  272.  
  273.         case DHCP_OPTION_OVERLOAD:
  274.         /* FIXME: we should pay attention to this */
  275.         logMessage("dhcp overload option is currently ignored!");
  276.         break;
  277.     }
  278.  
  279.     chptr += length;
  280.     }
  281.  
  282.     setMissingIpInfo(intf);
  283. }
  284.  
  285. static char * perrorstr(char * msg) {
  286.     static char * err = NULL;
  287.     static int errsize = 0;
  288.     static int newsize;
  289.  
  290.     newsize = strlen(msg) + strlen(strerror(errno)) + 3;
  291.     if (!errsize) {
  292.     errsize = newsize;
  293.     err = malloc(errsize);
  294.     } else if (errsize < newsize) {
  295.     free(err);
  296.     errsize = newsize;
  297.     err = malloc(errsize);
  298.     } 
  299.  
  300.     sprintf(err, "%s: %s", msg, strerror(errno));
  301.  
  302.     return err;
  303. }
  304.  
  305. static void initVendorCodes(struct bootpRequest * breq) {
  306.     memcpy(breq->vendor, vendCookie, sizeof(vendCookie));
  307. }
  308.  
  309. static char * prepareRequest(struct bootpRequest * breq,
  310.                  int sock, char * device, time_t startTime) {
  311.     struct ifreq req;
  312.     int i;
  313.  
  314.     memset(breq, 0, sizeof(*breq));
  315.  
  316.     breq->opcode = BOOTP_OPCODE_REQUEST;
  317.  
  318.     strcpy(req.ifr_name, device);
  319.     if (ioctl(sock, SIOCGIFHWADDR, &req))
  320.     return perrorstr("SIOCSIFHWADDR");
  321.  
  322.     breq->hw = 1;         /* ethernet */
  323.     breq->hwlength = IFHWADDRLEN;    
  324.     memcpy(breq->hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN);
  325.  
  326.     /* we should use something random here, but I don't want to start using
  327.        stuff from the math library */
  328.     breq->id = time(NULL);
  329.     for (i = 0; i < IFHWADDRLEN; i++)
  330.     breq->id ^= breq->hwaddr[i] << (8 * (i % 4));
  331.  
  332.     breq->hopcount = 0;
  333.     breq->secs = time(NULL) - startTime;
  334.  
  335.     initVendorCodes(breq);
  336.  
  337.     return NULL;
  338. }
  339.  
  340. static char * handleTransaction(int s, int timeout, struct bootpRequest * breq,
  341.                     struct bootpRequest * bresp, 
  342.                     struct sockaddr_in * serverAddr,
  343.                 struct sockaddr_in * respondant,
  344.                 int dhcpType) {
  345.     struct timeval tv;
  346.     fd_set readfs;
  347.     int i;
  348.     struct sockaddr_in tmpAddress;
  349.     int gotit = 0;
  350.     int tries = NUM_RETRIES;
  351.     int nextTimeout = 4;
  352.  
  353.     while (!gotit && tries--) {
  354.     i = sizeof(*breq);
  355.     if (dhcpType == -1)
  356.         i -= (DHCP_VENDOR_LENGTH - BOOTP_VENDOR_LENGTH);
  357.  
  358.     if (sendto(s, breq, i, 0, (struct sockaddr *) serverAddr, 
  359.            sizeof(*serverAddr)) != i) {
  360.         close(s);
  361.         return perrorstr("sendto");
  362.     }
  363.  
  364.     tv.tv_usec = 0;
  365.      tv.tv_sec = nextTimeout;
  366.     if (tv.tv_sec > timeout) tv.tv_sec = timeout;
  367.     timeout -= tv.tv_sec;
  368.     nextTimeout *= 2;
  369.     switch (time(NULL) & 4) {
  370.         case 0:    nextTimeout--; break;
  371.         case 1:    nextTimeout++; break;
  372.     }
  373.  
  374.     FD_ZERO(&readfs);
  375.     FD_SET(s, &readfs);
  376.     switch ((select(s + 1, &readfs, NULL, NULL, &tv))) {
  377.       case 0:
  378.         break;
  379.  
  380.       case 1:
  381.         i = sizeof(tmpAddress);
  382.         if (recvfrom(s, bresp, sizeof(*bresp), 0, 
  383.              (struct sockaddr *) &tmpAddress, &i) < 0)
  384.         return perrorstr("recvfrom");
  385.  
  386.         /* sanity checks */
  387.         if (bresp->id != breq->id) continue;
  388.         if (bresp->opcode != BOOTP_OPCODE_REPLY) continue;
  389.         if (bresp->hwlength != breq->hwlength) continue;
  390.         if (memcmp(bresp->hwaddr, breq->hwaddr, bresp->hwlength)) continue;
  391.         if (dhcpMessageType(bresp) != dhcpType) continue;
  392.         if (memcmp(bresp->vendor, vendCookie, 4)) continue;
  393.         if (respondant) *respondant = tmpAddress;
  394.         gotit = 1;
  395.  
  396.         break;
  397.  
  398.       default:
  399.         return perrorstr("select");
  400.     }
  401.     }
  402.  
  403.     if (!gotit)
  404.     if (dhcpType == -1)
  405.     return _("No BOOTP reply received");
  406.     else
  407.     return _("No DHCP reply received");
  408.  
  409.     return NULL;
  410. }
  411.  
  412. static void addVendorCode(struct bootpRequest * breq, unsigned char option,
  413.               unsigned char length, void * data) {
  414.     unsigned char * chptr;
  415.     int theOption, theLength;
  416.  
  417.     chptr = breq->vendor;
  418.     chptr += 4;
  419.     while (*chptr != 0xFF && *chptr != option) {
  420.     theOption = *chptr++;
  421.     if (!theOption) continue;
  422.     theLength = *chptr++;
  423.     chptr += theLength;
  424.     }
  425.  
  426.     *chptr++ = option;
  427.     *chptr++ = length;
  428.     memcpy(chptr, data, length);
  429.     chptr[length] = 0xff;
  430. }
  431.  
  432. static int getVendorCode(struct bootpRequest * bresp, unsigned char option,
  433.               void * data) {
  434.     unsigned char * chptr;
  435.     unsigned int length, theOption;
  436.  
  437.     chptr = bresp->vendor;
  438.     chptr += 4;
  439.     while (*chptr != 0xFF && *chptr != option) {
  440.     theOption = *chptr++;
  441.     if (!theOption) continue;
  442.     length = *chptr++;
  443.     chptr += length;
  444.     }
  445.  
  446.     if (*chptr++ == 0xff) return 1;
  447.  
  448.     length = *chptr++;
  449.     memcpy(data, chptr, length);
  450.  
  451.     return 0;
  452. }
  453.  
  454. static void hareyCarey(int signo) {
  455.     logMessage("DHCP FAILED TO RENEW LEASE -- KILLING INSTALL");
  456.     kill(getppid(), SIGFPE);
  457. }
  458.  
  459. /* This is somewhat broken. We try only to renew the lease. If we fail,
  460.    we don't try to completely rebind. This doesn't follow the DHCP spec,
  461.    but for the install it should be a reasonable compromise. */
  462. static void dhcpDaemon(int s, struct bootpRequest * origResult, char * device) {
  463.     unsigned int t1, lease;        /* offsets from "now" */
  464.     time_t now;
  465.     struct bootpRequest breq, bresp;
  466.     unsigned char messageType;
  467.     struct sockaddr_in serverAddr;
  468.     char * chptr;
  469.     int renewLease = 1;
  470.  
  471.     while (renewLease) {
  472.     now = time(NULL);
  473.     
  474.     if (getVendorCode(origResult, DHCP_OPTION_LEASE, &lease))
  475.         renewLease = 0;
  476.  
  477.     if (getVendorCode(origResult, DHCP_OPTION_T1, &t1)) {
  478.         /* this is equiv to t1 = 0.875 * lease, which is mandated by RFC */
  479.         t1 = (7 * lease) >> 3;
  480.     }
  481.  
  482.     t1 = 120;
  483.     /* if this goes off, we have a problem! */
  484.     signal(SIGALRM, hareyCarey);
  485.     alarm((now + lease) - time(NULL));
  486.  
  487.     /* go to sleep until we're needed again */
  488.     sleep((now + t1) - time(NULL));
  489.  
  490.     if ((chptr = prepareRequest(&breq, s, device, time(NULL)))) {
  491.         close(s);
  492.         while (1);    /* problem */
  493.     }
  494.  
  495.     messageType = DHCP_TYPE_REQUEST;
  496.     addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
  497.     breq.ciaddr = origResult->yiaddr;
  498.  
  499.     serverAddr.sin_family = AF_INET;
  500.     serverAddr.sin_port = htons(BOOTP_SERVER_PORT);    /* bootp server */
  501.     getVendorCode(origResult, DHCP_OPTION_SERVER, &serverAddr.sin_addr);
  502.      
  503.     if ((chptr = handleTransaction(s, 60, &breq, &bresp, &serverAddr,
  504.                        NULL, DHCP_TYPE_ACK))) {
  505.         renewLease = 0;
  506.     } else {
  507.         *origResult = bresp;
  508.     }
  509.     }
  510.  
  511.     #ifdef STANDALONE
  512.     printf("dhcp renewel failed\n");
  513.     #else
  514.     logMessage("DHCP FAILED TO RENEW LEASE -- KILLING INSTALL");
  515.     kill(getppid(), SIGFPE);
  516.     #endif
  517. }
  518.  
  519. char * doDhcp(int timeout, struct intfInfo * intf, struct netInfo * net,
  520.         int shouldDaemonize) {
  521.     int s, i;
  522.     struct sockaddr_in serverAddr;
  523.     struct sockaddr_in clientAddr;
  524.     struct sockaddr_in broadcastAddr;
  525.     struct bootpRequest breq, bresp;
  526.     unsigned char * chptr;
  527.     unsigned char messageType;
  528.     unsigned int lease;
  529.     time_t startTime = time(NULL);
  530.  
  531.     s = socket(AF_INET, SOCK_DGRAM, 0);
  532.     if (s < 0) {
  533.     return perrorstr("socket");
  534.     }
  535.  
  536.     if ((chptr = setupInterface(intf->device, s))) {
  537.     close(s);
  538.     return chptr;
  539.     }
  540.  
  541.     if ((chptr = prepareRequest(&breq, s, intf->device, startTime))) {
  542.     close(s);
  543.     return chptr;
  544.     }
  545.  
  546.     messageType = DHCP_TYPE_DISCOVER;
  547.     addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
  548.  
  549.     memset(&clientAddr.sin_addr, 0, sizeof(&clientAddr.sin_addr));
  550.     clientAddr.sin_family = AF_INET;
  551.     clientAddr.sin_port = htons(BOOTP_CLIENT_PORT);    /* bootp client */
  552.  
  553.     if (bind(s, (struct sockaddr *) &clientAddr, sizeof(clientAddr))) {
  554.     return perrorstr("bind");
  555.     }
  556.  
  557.     broadcastAddr.sin_family = AF_INET;
  558.     broadcastAddr.sin_port = htons(BOOTP_SERVER_PORT);    /* bootp server */
  559.     memset(&broadcastAddr.sin_addr, 0xff, 
  560.        sizeof(broadcastAddr.sin_addr));  /* broadcast */
  561.  
  562.     if ((chptr = handleTransaction(s, timeout, &breq, &bresp, &broadcastAddr,
  563.                    NULL, DHCP_TYPE_OFFER))) {
  564.     close(s);
  565.     return chptr;
  566.     }
  567.  
  568.     serverAddr.sin_family = AF_INET;
  569.     serverAddr.sin_port = htons(BOOTP_SERVER_PORT);    /* bootp server */
  570.     if (getVendorCode(&bresp, DHCP_OPTION_SERVER, &serverAddr.sin_addr)) {
  571.     close(s);
  572.     return "DHCPOFFER didn't include server address";
  573.     }
  574.  
  575.     initVendorCodes(&breq);
  576.     messageType = DHCP_TYPE_REQUEST;
  577.     addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
  578.     addVendorCode(&breq, DHCP_OPTION_SERVER, 4, &serverAddr.sin_addr);
  579.     addVendorCode(&breq, DHCP_OPTION_REQADDR, 4, &bresp.yiaddr);
  580.  
  581.     /* request a lease of 1 hour */
  582.     i = htonl(60 * 60);
  583.     addVendorCode(&breq, DHCP_OPTION_LEASE, 4, &i);
  584.     
  585.     if ((chptr = handleTransaction(s, timeout, &breq, &bresp, &broadcastAddr,
  586.                    NULL, DHCP_TYPE_ACK))) {
  587.     close(s);
  588.     return chptr;
  589.     }
  590.  
  591.     if (getVendorCode(&bresp, DHCP_OPTION_LEASE, &lease))
  592.     return "failed to get lease time\n";
  593.     lease = ntohl(lease);
  594.  
  595.     if (lease && lease != 0xffffffff && shouldDaemonize) {
  596. #ifndef STANDALONE
  597.     if (!fork())
  598. #endif
  599.         dhcpDaemon(s, &bresp, intf->device);
  600.     }
  601.  
  602.     close(s);
  603.  
  604.     parseReply(&bresp, intf, net);
  605.  
  606.     if (!(intf->set & INTFINFO_HAS_BOOTSERVER)) {
  607.     intf->bootServer = serverAddr.sin_addr;
  608.     intf->set |= INTFINFO_HAS_BOOTSERVER;
  609.     }
  610.  
  611.     return NULL;
  612. }
  613.  
  614. char * doBootp(int timeout, struct intfInfo * intf, struct netInfo * net) {
  615.     int s;
  616.     struct sockaddr_in clientAddr;
  617.     struct sockaddr_in broadcastAddr;
  618.     struct sockaddr_in serverAddr;
  619.     struct bootpRequest breq, bresp;
  620.     unsigned char * chptr;
  621.     time_t startTime = time(NULL);
  622.  
  623.     s = socket(AF_INET, SOCK_DGRAM, 0);
  624.     if (s < 0) {
  625.     return perrorstr("socket");
  626.     }
  627.  
  628.     if ((chptr = setupInterface(intf->device, s))) {
  629.     close(s);
  630.     return chptr;
  631.     }
  632.  
  633.     if ((chptr = prepareRequest(&breq, s, intf->device, startTime))) {
  634.     close(s);
  635.     return chptr;
  636.     }
  637.  
  638.     memset(&clientAddr.sin_addr, 0, sizeof(&clientAddr.sin_addr));
  639.     clientAddr.sin_family = AF_INET;
  640.     clientAddr.sin_port = htons(BOOTP_CLIENT_PORT);    /* bootp client */
  641.  
  642.     if (bind(s, (struct sockaddr *) &clientAddr, sizeof(clientAddr))) {
  643.     return perrorstr("bind");
  644.     }
  645.  
  646.     broadcastAddr.sin_family = AF_INET;
  647.     broadcastAddr.sin_port = htons(BOOTP_SERVER_PORT);    /* bootp server */
  648.     memset(&broadcastAddr.sin_addr, 0xff, 
  649.        sizeof(broadcastAddr.sin_addr));  /* broadcast */
  650.  
  651.     if ((chptr = handleTransaction(s, timeout, &breq, &bresp, &broadcastAddr,
  652.                    &serverAddr, -1))) {
  653.     close(s);
  654.     return chptr;
  655.     }
  656.  
  657.     close(s);
  658.  
  659.     parseReply(&bresp, intf, net);
  660.  
  661.     if (!(intf->set & INTFINFO_HAS_BOOTSERVER)) {
  662.     intf->bootServer = serverAddr.sin_addr;
  663.     intf->set |= INTFINFO_HAS_BOOTSERVER;
  664.     }
  665.  
  666.     return NULL;
  667. }
  668.  
  669. #ifdef STANDALONE
  670. void main (void) {
  671.     char * msg;
  672.     struct intfInfo intf;
  673.     struct netInfo net;
  674.  
  675.     memset(&intf, 0, sizeof(intf));
  676.     memset(&net, 0, sizeof(net));
  677.     strcpy(intf.device, "eth0");
  678.  
  679. #ifndef USEBOOTP
  680.     msg = doDhcp(30, &intf, &net);
  681. #else
  682.     msg = doBootp(30, &intf, &net);
  683. #endif
  684.  
  685.     if (msg) {
  686.      puts(msg);
  687.      exit(1);
  688.     }
  689.  
  690.     printf("got address %s\n", inet_ntoa(intf.ip));
  691.     if (intf.set & INTFINFO_HAS_NETMASK)
  692.     printf("got netmask %s\n", inet_ntoa(intf.netmask));
  693.     if (net.set & NETINFO_HAS_DOMAIN)
  694.     printf("got domain %s\n", net.domain);
  695. }
  696. #endif
  697.