wol.c (6520B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "config.h" 21 22#include "guacamole/error.h" 23#include "guacamole/wol.h" 24 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28#include <unistd.h> 29#include <arpa/inet.h> 30#include <netinet/in.h> 31#include <sys/socket.h> 32 33/** 34 * Generate the magic Wake-on-LAN (WoL) packet for the specified MAC address 35 * and place it in the character array. 36 * 37 * @param packet 38 * The character array that will contain the generated packet. 39 * 40 * @param mac_address 41 * The unsigned int representation of the MAC address to place in the packet. 42 */ 43static void __guac_wol_create_magic_packet(unsigned char packet[], 44 unsigned int mac_address[]) { 45 46 int i; 47 unsigned char mac[6]; 48 49 /* Concurrently fill the first part of the packet with 0xFF, and copy the 50 MAC address from the int array to the char array. */ 51 for (i = 0; i < 6; i++) { 52 packet[i] = 0xFF; 53 mac[i] = mac_address[i]; 54 } 55 56 /* Copy the MAC address contents into the char array that is storing 57 the rest of the packet. */ 58 for (i = 1; i <= 16; i++) { 59 memcpy(&packet[i * 6], &mac, 6 * sizeof(unsigned char)); 60 } 61 62} 63 64/** 65 * Send the magic Wake-on-LAN (WoL) packet to the specified broadcast address, 66 * returning the number of bytes sent, or zero if any error occurred and nothing 67 * was sent. 68 * 69 * @param broadcast_addr 70 * The broadcast address to which to send the magic WoL packet. 71 * 72 * @param udp_port 73 * The UDP port to use when sending the WoL packet. 74 * 75 * @param packet 76 * The magic WoL packet to send. 77 * 78 * @return 79 * The number of bytes sent, or zero if nothing could be sent. 80 */ 81static ssize_t __guac_wol_send_packet(const char* broadcast_addr, 82 const unsigned short udp_port, unsigned char packet[]) { 83 84 struct sockaddr_in wol_dest; 85 int wol_socket; 86 87 /* Determine the IP version, starting with IPv4. */ 88 wol_dest.sin_port = htons(udp_port); 89 wol_dest.sin_family = AF_INET; 90 int retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr)); 91 92 /* If return value is less than zero, this system doesn't know about IPv4. */ 93 if (retval < 0) { 94 guac_error = GUAC_STATUS_SEE_ERRNO; 95 guac_error_message = "IPv4 address family is not supported"; 96 return 0; 97 } 98 99 /* If return value is zero, address doesn't match the IPv4, so try IPv6. */ 100 else if (retval == 0) { 101 wol_dest.sin_family = AF_INET6; 102 retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr)); 103 104 /* System does not support IPv6. */ 105 if (retval < 0) { 106 guac_error = GUAC_STATUS_SEE_ERRNO; 107 guac_error_message = "IPv6 address family is not supported"; 108 return 0; 109 } 110 111 /* Address didn't match IPv6. */ 112 else if (retval == 0) { 113 guac_error = GUAC_STATUS_INVALID_ARGUMENT; 114 guac_error_message = "Invalid broadcast or multicast address specified for Wake-on-LAN"; 115 return 0; 116 } 117 } 118 119 120 121 /* Set up the socket */ 122 wol_socket = socket(wol_dest.sin_family, SOCK_DGRAM, 0); 123 124 /* If socket open fails, bail out. */ 125 if (wol_socket < 0) { 126 guac_error = GUAC_STATUS_SEE_ERRNO; 127 guac_error_message = "Failed to open socket to send Wake-on-LAN packet"; 128 return 0; 129 } 130 131 /* Set up socket for IPv4 broadcast. */ 132 if (wol_dest.sin_family == AF_INET) { 133 134 /* For configuring socket broadcast */ 135 int wol_bcast = 1; 136 137 /* Attempt to set IPv4 broadcast; exit with error if this fails. */ 138 if (setsockopt(wol_socket, SOL_SOCKET, SO_BROADCAST, &wol_bcast, 139 sizeof(wol_bcast)) < 0) { 140 close(wol_socket); 141 guac_error = GUAC_STATUS_SEE_ERRNO; 142 guac_error_message = "Failed to set IPv4 broadcast for Wake-on-LAN socket"; 143 return 0; 144 } 145 } 146 147 /* Set up socket for IPv6 multicast. */ 148 else { 149 150 /* Stick to a single hop for now. */ 151 int hops = 1; 152 153 /* Attempt to set IPv6 multicast; exit with error if this fails. */ 154 if (setsockopt(wol_socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, 155 sizeof(hops)) < 0) { 156 close(wol_socket); 157 guac_error = GUAC_STATUS_SEE_ERRNO; 158 guac_error_message = "Failed to set IPv6 multicast for Wake-on-LAN socket"; 159 return 0; 160 } 161 } 162 163 /* Send the packet and return number of bytes sent. */ 164 int bytes = sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0, 165 (struct sockaddr*) &wol_dest, sizeof(wol_dest)); 166 close(wol_socket); 167 return bytes; 168 169} 170 171int guac_wol_wake(const char* mac_addr, const char* broadcast_addr, 172 const unsigned short udp_port) { 173 174 unsigned char wol_packet[GUAC_WOL_PACKET_SIZE]; 175 unsigned int dest_mac[6]; 176 177 /* Parse mac address and return with error if parsing fails. */ 178 if (sscanf(mac_addr, "%x:%x:%x:%x:%x:%x", 179 &(dest_mac[0]), &(dest_mac[1]), &(dest_mac[2]), 180 &(dest_mac[3]), &(dest_mac[4]), &(dest_mac[5])) != 6) { 181 guac_error = GUAC_STATUS_INVALID_ARGUMENT; 182 guac_error_message = "Invalid argument for Wake-on-LAN MAC address"; 183 return -1; 184 } 185 186 /* Generate the magic packet. */ 187 __guac_wol_create_magic_packet(wol_packet, dest_mac); 188 189 /* Send the packet and record bytes sent. */ 190 int bytes_sent = __guac_wol_send_packet(broadcast_addr, udp_port, 191 wol_packet); 192 193 /* Return 0 if bytes were sent, otherwise return an error. */ 194 if (bytes_sent) 195 return 0; 196 197 return -1; 198}