File indexing completed on 2024-10-13 12:15:21
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 }