File indexing completed on 2024-04-28 15:27:12

0001 /*
0002     This file is part of the KDE Libraries
0003     SPDX-FileCopyrightText: 2001 Malte Starostik <malte@kde.org>
0004     SPDX-FileCopyrightText: 2011 Dawit Alemayehu <adawit@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #define _BSD_SOURCE /* setgroups */
0010 #define _DEFAULT_SOURCE /* stop glibc whining about the previous line */
0011 
0012 #include <arpa/inet.h>
0013 #include <net/if.h>
0014 #include <netinet/in.h>
0015 #include <sys/socket.h>
0016 #include <sys/time.h>
0017 #include <sys/types.h>
0018 
0019 #include <grp.h>
0020 #include <ifaddrs.h>
0021 #include <netdb.h>
0022 #include <stdio.h>
0023 #include <stdlib.h>
0024 #include <string.h>
0025 #include <time.h>
0026 #include <unistd.h>
0027 
0028 #include "dhcp.h"
0029 
0030 #ifndef INADDR_NONE /* some OSes don't define this */
0031 #define INADDR_NONE -1
0032 #endif
0033 
0034 static int set_gid(gid_t);
0035 static int set_uid(uid_t);
0036 static int get_port(const char *);
0037 static int init_socket(void);
0038 static uint32_t send_request(int);
0039 static void get_reply(int, uint32_t);
0040 
0041 static int set_gid(gid_t gid)
0042 {
0043     if (setgroups(1, &gid) == -1) {
0044         return -1;
0045     }
0046     return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
0047 }
0048 
0049 static int set_uid(uid_t uid)
0050 {
0051     return setuid(uid);
0052 }
0053 
0054 /* All functions below do an exit(1) on the slightest error */
0055 
0056 /* Returns the UDP port number for the given service name */
0057 static int get_port(const char *service)
0058 {
0059     struct servent *serv = getservbyname(service, "udp");
0060     if (serv == NULL) {
0061         exit(1);
0062     }
0063 
0064     return serv->s_port;
0065 }
0066 
0067 /* Opens the UDP socket, binds to the bootpc port and drops root privileges */
0068 static int init_socket()
0069 {
0070     struct sockaddr_in addr;
0071     struct protoent *proto;
0072     int sock;
0073     int bcast = 1;
0074 
0075     memset(&addr, 0, sizeof(addr));
0076     addr.sin_family = AF_INET;
0077     addr.sin_addr.s_addr = INADDR_ANY;
0078     addr.sin_port = get_port("bootpc");
0079 
0080     if ((proto = getprotobyname("udp")) == NULL || (sock = socket(AF_INET, SOCK_DGRAM, proto->p_proto)) == -1) {
0081         exit(1);
0082     }
0083 
0084     if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) == -1 || setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &bcast, sizeof(bcast)) == -1
0085         || bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
0086         exit(1);
0087     }
0088 
0089     if (set_gid(getgid()) != 0 || /* we don't need it anymore */
0090         set_uid(getuid()) != 0) {
0091         exit(1);
0092     }
0093     return sock;
0094 }
0095 
0096 struct response {
0097     uint32_t result;
0098     uint16_t err;
0099 };
0100 
0101 static struct response send_request_for(int sock, const char *hostname)
0102 {
0103     struct sockaddr_in addr;
0104     struct in_addr inaddr;
0105     struct dhcp_msg request;
0106     uint8_t *offs = request.options;
0107     struct response r;
0108 
0109     if (!inet_aton(hostname, &inaddr)) {
0110         r.err = -1;
0111         r.result = 0;
0112     }
0113 
0114     memset(&addr, 0, sizeof(addr));
0115     addr.sin_family = AF_INET;
0116     addr.sin_addr.s_addr = INADDR_NONE;
0117     addr.sin_port = get_port("bootps");
0118 
0119     memset(&request, 0, sizeof(request));
0120     request.op = DHCP_BOOTREQUEST;
0121     srand(time(NULL));
0122     request.xid = rand();
0123     request.ciaddr = (uint32_t)inaddr.s_addr;
0124 
0125     *offs++ = DHCP_MAGIC1;
0126     *offs++ = DHCP_MAGIC2;
0127     *offs++ = DHCP_MAGIC3;
0128     *offs++ = DHCP_MAGIC4;
0129 
0130     *offs++ = DHCP_OPT_MSGTYPE;
0131     *offs++ = 1; /* length */
0132     *offs++ = DHCP_INFORM;
0133 
0134     *offs++ = DHCP_OPT_PARAMREQ;
0135     *offs++ = 1; /* length */
0136     *offs++ = DHCP_OPT_WPAD;
0137 
0138     *offs++ = DHCP_OPT_END;
0139 
0140     if (sendto(sock, &request, sizeof(request), 0, (struct sockaddr *)&addr, sizeof(addr)) != sizeof(request)) {
0141         r.err = -1;
0142         r.result = 0;
0143     }
0144 
0145     r.err = 0;
0146     r.result = request.xid;
0147     return r;
0148 }
0149 
0150 /* Fills the DHCPINFORM request packet, returns the transaction id */
0151 static uint32_t send_request(int sock)
0152 {
0153     char hostname[NI_MAXHOST];
0154     struct ifaddrs *ifaddr;
0155     struct ifaddrs *ifa;
0156     int status = -1;
0157 
0158     if (getifaddrs(&ifaddr) == -1) {
0159         exit(1);
0160     }
0161 
0162     for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
0163         if (ifa->ifa_addr == NULL) {
0164             continue;
0165         }
0166 
0167         if (ifa->ifa_flags & IFF_LOOPBACK) {
0168             continue;
0169         }
0170 
0171         if (ifa->ifa_addr->sa_family != AF_INET) {
0172             continue;
0173         }
0174 
0175         status = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
0176 
0177         if (status != 0) {
0178             continue;
0179         }
0180 
0181         struct response r = send_request_for(sock, hostname);
0182 
0183         if (r.err != 0) {
0184             continue;
0185         }
0186 
0187         status = r.result;
0188     }
0189 
0190     freeifaddrs(ifaddr);
0191 
0192     if (status == -1) {
0193         exit(1);
0194     }
0195 
0196     return status;
0197 }
0198 
0199 /* Reads the reply from the socket, checks it and outputs the URL to STDOUT */
0200 static void get_reply(int sock, uint32_t xid)
0201 {
0202     struct dhcp_msg reply;
0203     int len;
0204     char wpad[DHCP_OPT_LEN + 1];
0205     uint8_t wpad_len;
0206     uint8_t *offs = reply.options;
0207     uint8_t *end;
0208 
0209     if ((len = recvfrom(sock, &reply, sizeof(reply), 0, NULL, NULL)) <= 0) {
0210         exit(1);
0211     }
0212 
0213     end = (uint8_t *)&reply + len;
0214     if (end < offs + 4 || end > &reply.options[DHCP_OPT_LEN] || reply.op != DHCP_BOOTREPLY || reply.xid != xid || *offs++ != DHCP_MAGIC1
0215         || *offs++ != DHCP_MAGIC2 || *offs++ != DHCP_MAGIC3 || *offs++ != DHCP_MAGIC4) {
0216         exit(1);
0217     }
0218 
0219     for (; offs < end - 1; offs += *offs + 1) {
0220         switch (*offs++) {
0221         case DHCP_OPT_END:
0222             exit(1);
0223         case DHCP_OPT_MSGTYPE:
0224             if (*offs != 1 || (offs >= end - 1) || *(offs + 1) != DHCP_ACK) {
0225                 exit(1);
0226             }
0227             break;
0228         case DHCP_OPT_WPAD:
0229             memset(wpad, 0, sizeof(wpad));
0230             wpad_len = *offs++;
0231             if (offs >= end) {
0232                 exit(1);
0233             }
0234             if (wpad_len > end - offs) {
0235                 wpad_len = end - offs;
0236             }
0237             strncpy(wpad, (char *)offs, wpad_len);
0238             wpad[wpad_len] = 0;
0239             printf("%s\n", wpad);
0240             close(sock);
0241             exit(0);
0242         }
0243     }
0244     exit(1);
0245 }
0246 
0247 int main()
0248 {
0249     fd_set rfds;
0250     struct timeval tv;
0251     int sock;
0252     uint32_t xid;
0253 
0254     sock = init_socket();
0255     xid = send_request(sock);
0256 
0257     FD_ZERO(&rfds);
0258     FD_SET(sock, &rfds);
0259     tv.tv_sec = 5;
0260     tv.tv_usec = 0;
0261     if (select(sock + 1, &rfds, NULL, NULL, &tv) == 1 && FD_ISSET(sock, &rfds)) {
0262         get_reply(sock, xid);
0263     }
0264 
0265     close(sock);
0266     exit(1);
0267 }